File indexing completed on 2024-05-12 04:39:08

0001 /*
0002     SPDX-FileCopyrightText: 2014 Milian Wolff <mail@milianw.de>
0003     SPDX-FileCopyrightText: 2015 Sergey Kalinichev <kalinichev.so.0@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "context.h"
0009 
0010 #include <QHash>
0011 #include <QRegularExpression>
0012 #include <QSet>
0013 #include <QStandardPaths>
0014 
0015 #include <interfaces/icore.h>
0016 #include <interfaces/idocumentcontroller.h>
0017 #include <interfaces/iprojectcontroller.h>
0018 #include <interfaces/iproject.h>
0019 
0020 #include <language/duchain/duchainlock.h>
0021 #include <language/duchain/ducontext.h>
0022 #include <language/duchain/topducontext.h>
0023 #include <language/duchain/declaration.h>
0024 #include <language/duchain/classmemberdeclaration.h>
0025 #include <language/duchain/classdeclaration.h>
0026 #include <language/duchain/duchainutils.h>
0027 #include <language/duchain/persistentsymboltable.h>
0028 #include <language/duchain/types/integraltype.h>
0029 #include <language/duchain/types/functiontype.h>
0030 #include <language/duchain/types/pointertype.h>
0031 #include <language/duchain/types/typealiastype.h>
0032 #include <language/duchain/types/typeutils.h>
0033 #include <language/duchain/stringhelpers.h>
0034 #include <language/codecompletion/codecompletionmodel.h>
0035 #include <language/codecompletion/normaldeclarationcompletionitem.h>
0036 #include <language/codegen/documentchangeset.h>
0037 #include <util/foregroundlock.h>
0038 #include <custom-definesandincludes/idefinesandincludesmanager.h>
0039 #include <project/projectmodel.h>
0040 
0041 #include "../util/clangdebug.h"
0042 #include "../util/clangtypes.h"
0043 #include "../util/clangutils.h"
0044 #include "../duchain/clangdiagnosticevaluator.h"
0045 #include "../duchain/parsesession.h"
0046 #include "../duchain/duchainutils.h"
0047 #include "../duchain/navigationwidget.h"
0048 #include "../clangsettings/clangsettingsmanager.h"
0049 
0050 #include <algorithm>
0051 #include <cstring>
0052 #include <functional>
0053 #include <memory>
0054 
0055 #include <KTextEditor/Document>
0056 #include <KTextEditor/View>
0057 
0058 using namespace KDevelop;
0059 
0060 namespace {
0061 /// Maximum return-type string length in completion items
0062 const int MAX_RETURN_TYPE_STRING_LENGTH = 20;
0063 
0064 /// Priority of code-completion results. NOTE: Keep in sync with Clang code base.
0065 enum CodeCompletionPriority {
0066   /// Priority for the next initialization in a constructor initializer list.
0067   CCP_NextInitializer = 7,
0068   /// Priority for an enumeration constant inside a switch whose condition is of the enumeration type.
0069   CCP_EnumInCase = 7,
0070 
0071   CCP_LocalDeclarationMatch = 8,
0072 
0073   CCP_DeclarationMatch = 12,
0074 
0075   CCP_LocalDeclarationSimiliar = 17,
0076   /// Priority for a send-to-super completion.
0077   CCP_SuperCompletion = 20,
0078 
0079   CCP_DeclarationSimiliar = 25,
0080   /// Priority for a declaration that is in the local scope.
0081   CCP_LocalDeclaration = 34,
0082   /// Priority for a member declaration found from the current method or member function.
0083   CCP_MemberDeclaration = 35,
0084   /// Priority for a language keyword (that isn't any of the other categories).
0085   CCP_Keyword = 40,
0086   /// Priority for a code pattern.
0087   CCP_CodePattern = 40,
0088   /// Priority for a non-type declaration.
0089   CCP_Declaration = 50,
0090   /// Priority for a type.
0091   CCP_Type = CCP_Declaration,
0092   /// Priority for a constant value (e.g., enumerator).
0093   CCP_Constant = 65,
0094   /// Priority for a preprocessor macro.
0095   CCP_Macro = 70,
0096   /// Priority for a nested-name-specifier.
0097   CCP_NestedNameSpecifier = 75,
0098   /// Priority for a result that isn't likely to be what the user wants, but is included for completeness.
0099   CCP_Unlikely = 80
0100 };
0101 
0102 /**
0103  * Common base class for Clang code completion items.
0104  */
0105 template<class Base>
0106 class CompletionItem : public Base
0107 {
0108 public:
0109     CompletionItem(const QString& display, const QString& prefix)
0110         : Base()
0111         , m_display(display)
0112         , m_prefix(prefix)
0113         , m_unimportant(false)
0114     {
0115     }
0116 
0117     ~CompletionItem() override = default;
0118 
0119     QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* /*model*/) const override
0120     {
0121         if (role == Qt::DisplayRole) {
0122             if (index.column() == CodeCompletionModel::Prefix) {
0123                 return m_prefix;
0124             } else if (index.column() == CodeCompletionModel::Name) {
0125                 return m_display;
0126             }
0127         }
0128         return {};
0129     }
0130 
0131     void markAsUnimportant()
0132     {
0133         m_unimportant = true;
0134     }
0135 
0136 protected:
0137     QString m_display;
0138     QString m_prefix;
0139     bool m_unimportant;
0140 };
0141 
0142 class OverrideItem : public CompletionItem<CompletionTreeItem>
0143 {
0144 public:
0145     OverrideItem(const QString& nameAndParams, const QString& returnType)
0146         : CompletionItem<CompletionTreeItem>(
0147               nameAndParams,
0148               i18n("Override %1", returnType)
0149           )
0150         , m_returnType(returnType)
0151     {
0152     }
0153 
0154     QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
0155     {
0156         if (role == Qt::DecorationRole) {
0157             if (index.column() == KTextEditor::CodeCompletionModel::Icon) {
0158                 return QIcon::fromTheme(QStringLiteral("CTparents"));
0159             }
0160         }
0161         return CompletionItem<CompletionTreeItem>::data(index, role, model);
0162     }
0163 
0164     void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
0165     {
0166         QString replacement = m_returnType + QLatin1Char(' ') + m_display.replace(QRegularExpression(QStringLiteral("\\s*=\\s*0")), QString());
0167 
0168         bool appendSpecifer = true;
0169         if (const auto* project =
0170             KDevelop::ICore::self()->projectController()->findProjectForUrl(view->document()->url())) {
0171             const auto arguments = KDevelop::IDefinesAndIncludesManager::manager()->parserArguments(
0172                 project->filesForPath(IndexedString(view->document()->url().path())).first());
0173             const auto match = QRegularExpression(QStringLiteral(R"(-std=c\+\+(\w+))")).match(arguments);
0174 
0175             appendSpecifer = match.hasMatch(); // assume non-modern if no standard is specified
0176             if (appendSpecifer) {
0177                 const auto standard = match.capturedRef(1);
0178                 appendSpecifer = (standard != QLatin1String("98") && standard != QLatin1String("03"));
0179             }
0180         }
0181 
0182         if (appendSpecifer) {
0183             replacement.append(QLatin1String(" override;"));
0184         } else {
0185             replacement.append(QLatin1Char(';'));
0186         }
0187 
0188         DocumentChange overrideChange(IndexedString(view->document()->url()),
0189                                             word,
0190                                             QString{},
0191                                             replacement);
0192         overrideChange.m_ignoreOldText = true;
0193         DocumentChangeSet changes;
0194         changes.addChange(overrideChange);
0195         changes.applyAllChanges();
0196     }
0197 
0198 private:
0199     QString m_returnType;
0200 };
0201 
0202 /**
0203  * Specialized completion item class for items which are represented by a Declaration
0204  */
0205 class DeclarationItem : public CompletionItem<NormalDeclarationCompletionItem>
0206 {
0207 public:
0208     DeclarationItem(Declaration* dec, const QString& display, const QString& prefix, const QString& replacement)
0209         : CompletionItem<NormalDeclarationCompletionItem>(display, prefix)
0210         , m_replacement(replacement)
0211     {
0212         m_declaration = dec;
0213     }
0214 
0215     QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
0216     {
0217         if (role == CodeCompletionModel::MatchQuality && m_matchQuality) {
0218             return m_matchQuality;
0219         }
0220 
0221         auto ret = CompletionItem<NormalDeclarationCompletionItem>::data(index, role, model);
0222         if (ret.isValid()) {
0223             return ret;
0224         }
0225         return NormalDeclarationCompletionItem::data(index, role, model);
0226     }
0227 
0228     void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
0229     {
0230         QString repl = m_replacement;
0231         DUChainReadLocker lock;
0232 
0233         if(!m_declaration){
0234             return;
0235         }
0236 
0237         if(m_declaration->isFunctionDeclaration()) {
0238             const auto functionType = m_declaration->type<FunctionType>();
0239 
0240             // protect against buggy code that created the m_declaration,
0241             // to mark it as a function but not assign a function type
0242             if (!functionType)
0243                 return;
0244 
0245             auto doc = view->document();
0246 
0247             // Function pointer?
0248             bool funcptr = false;
0249             const auto line = doc->line(word.start().line());
0250             auto pos = word.end().column() - 1;
0251             while ( pos > 0 && (line.at(pos).isLetterOrNumber() || line.at(pos) == QLatin1Char(':')) ) {
0252                 pos--;
0253                 if ( line.at(pos) == QLatin1Char('&') ) {
0254                     funcptr = true;
0255                     break;
0256                 }
0257             }
0258 
0259             auto restEmpty = doc->characterAt(word.end() + KTextEditor::Cursor{0, 1}) == QChar();
0260 
0261             bool didAddParentheses = false;
0262             if ( !funcptr && doc->characterAt(word.end()) != QLatin1Char('(') ) {
0263                 repl += QLatin1String("()");
0264                 didAddParentheses = true;
0265             }
0266             view->document()->replaceText(word, repl);
0267             if (functionType->indexedArgumentsSize() && didAddParentheses) {
0268                 view->setCursorPosition(word.start() + KTextEditor::Cursor(0, repl.size() - 1));
0269             }
0270             auto returnTypeIntegral = functionType->returnType().dynamicCast<IntegralType>();
0271             if ( restEmpty && !funcptr && returnTypeIntegral && returnTypeIntegral->dataType() == IntegralType::TypeVoid ) {
0272                 // function returns void and rest of line is empty -- nothing can be done with the result
0273                 if (functionType->indexedArgumentsSize() ) {
0274                     // we placed the cursor inside the ()
0275                     view->document()->insertText(view->cursorPosition() + KTextEditor::Cursor(0, 1), QStringLiteral(";"));
0276                 }
0277                 else {
0278                     // we placed the cursor after the ()
0279                     view->document()->insertText(view->cursorPosition(), QStringLiteral(";"));
0280                     view->setCursorPosition(view->cursorPosition() + KTextEditor::Cursor{0, 1});
0281                 }
0282             }
0283         } else {
0284             view->document()->replaceText(word, repl);
0285         }
0286     }
0287 
0288     bool createsExpandingWidget() const override
0289     {
0290         return true;
0291     }
0292 
0293     QWidget* createExpandingWidget(const CodeCompletionModel* /*model*/) const override
0294     {
0295         return new ClangNavigationWidget(m_declaration, AbstractNavigationWidget::EmbeddableWidget);
0296     }
0297 
0298     int matchQuality() const
0299     {
0300         return m_matchQuality;
0301     }
0302 
0303     ///Sets match quality from 0 to 10. 10 is the best fit.
0304     void setMatchQuality(int value)
0305     {
0306         m_matchQuality = value;
0307     }
0308 
0309     void setInheritanceDepth(int depth)
0310     {
0311         m_inheritanceDepth = depth;
0312     }
0313 
0314     int argumentHintDepth() const override
0315     {
0316         return m_depth;
0317     }
0318 
0319     void setArgumentHintDepth(int depth)
0320     {
0321         m_depth = depth;
0322     }
0323 
0324 protected:
0325     int m_matchQuality = 0;
0326     int m_depth = 0;
0327     QString m_replacement;
0328 };
0329 
0330 class ImplementsItem : public DeclarationItem
0331 {
0332 public:
0333     static QString replacement(const FuncImplementInfo& info)
0334     {
0335         QString replacement = info.templatePrefix;
0336         if (!info.isDestructor && !info.isConstructor) {
0337             replacement += info.returnType + QLatin1Char(' ');
0338         }
0339         replacement += info.prototype + QLatin1String("\n{\n}\n");
0340         return replacement;
0341     }
0342 
0343     explicit ImplementsItem(const FuncImplementInfo& item)
0344         : DeclarationItem(item.declaration.data(), item.prototype,
0345             i18n("Implement %1", item.isConstructor ? QStringLiteral("<constructor>") :
0346                                    item.isDestructor ? QStringLiteral("<destructor>") : item.returnType),
0347             replacement(item)
0348           )
0349     {
0350     }
0351 
0352     QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
0353     {
0354         if (index.column() == CodeCompletionModel::Arguments) {
0355             // our display string already contains the arguments
0356             return {};
0357         }
0358         return DeclarationItem::data(index, role, model);
0359     }
0360 
0361     void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
0362     {
0363         auto* const document = view->document();
0364 
0365         DocumentChangeSet changes;
0366         KTextEditor::Cursor rangeStart = word.start();
0367 
0368         // try and replace leading typed text that match the proposed implementation
0369         const QString leading = document->line(word.end().line()).left(word.end().column());
0370         const QString leadingNoSpace = removeWhitespace(leading);
0371         if (!leadingNoSpace.isEmpty() && (removeWhitespace(m_display).startsWith(leadingNoSpace)
0372             || removeWhitespace(m_replacement).startsWith(leadingNoSpace))) {
0373             const int removeSize = leading.end() - std::find_if_not(leading.begin(), leading.end(),
0374                                         [](QChar c){ return c.isSpace(); });
0375             rangeStart = {word.end().line(), word.end().column() - removeSize};
0376         }
0377 
0378         DocumentChange change(IndexedString(view->document()->url()),
0379                               KTextEditor::Range(rangeStart, word.end()),
0380                               QString(),
0381                               m_replacement);
0382         change.m_ignoreOldText = true;
0383         changes.addChange(change);
0384         changes.applyAllChanges();
0385 
0386         // Place cursor after the opening brace
0387         // arbitrarily chose 4, as it would accommodate the template and return types on their own line
0388         const auto searchRange = KTextEditor::Range(rangeStart, rangeStart.line() + 4, 0);
0389         const auto results = view->document()->searchText(searchRange, QStringLiteral("{"));
0390         if (!results.isEmpty()) {
0391             view->setCursorPosition(results.first().end());
0392         }
0393     }
0394 };
0395 
0396 class ArgumentHintItem : public DeclarationItem
0397 {
0398 public:
0399     struct CurrentArgumentRange
0400     {
0401         int start;
0402         int end;
0403     };
0404 
0405     ArgumentHintItem(Declaration* decl,  const QString& prefix, const QString& name, const QString& arguments, const CurrentArgumentRange& range)
0406         : DeclarationItem(decl, name, prefix, {})
0407         , m_range(range)
0408         , m_arguments(arguments)
0409     {}
0410 
0411     QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
0412     {
0413         if (role == CodeCompletionModel::CustomHighlight && index.column() == CodeCompletionModel::Arguments && argumentHintDepth()) {
0414             QTextCharFormat boldFormat;
0415             boldFormat.setFontWeight(QFont::Bold);
0416             const QList<QVariant> highlighting {
0417                 QVariant(m_range.start),
0418                 QVariant(m_range.end),
0419                 boldFormat,
0420             };
0421             return highlighting;
0422         }
0423 
0424         if (role == CodeCompletionModel::HighlightingMethod && index.column() == CodeCompletionModel::Arguments && argumentHintDepth()) {
0425             return QVariant(CodeCompletionModel::CustomHighlighting);
0426         }
0427 
0428         if (index.column() == CodeCompletionModel::Arguments) {
0429             return m_arguments;
0430         }
0431 
0432         return DeclarationItem::data(index, role, model);
0433     }
0434 
0435 private:
0436     CurrentArgumentRange m_range;
0437     QString m_arguments;
0438 };
0439 
0440 /**
0441  * A minimalistic completion item for macros and such
0442  */
0443 class SimpleItem : public CompletionItem<CompletionTreeItem>
0444 {
0445 public:
0446     SimpleItem(const QString& display, const QString& prefix, const QString& replacement, const QIcon& icon = QIcon())
0447         : CompletionItem<CompletionTreeItem>(display, prefix)
0448         , m_replacement(replacement)
0449         , m_icon(icon)
0450     {
0451     }
0452 
0453     void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
0454     {
0455         view->document()->replaceText(word, m_replacement);
0456     }
0457 
0458     QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
0459     {
0460         if (role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Icon) {
0461             return m_icon;
0462         }
0463         if (role == CodeCompletionModel::UnimportantItemRole) {
0464             return m_unimportant;
0465         }
0466         return CompletionItem<CompletionTreeItem>::data(index, role, model);
0467     }
0468 
0469 private:
0470     QString m_replacement;
0471     QIcon m_icon;
0472 };
0473 
0474 /**
0475  * Return true in case position @p position represents a cursor inside a comment
0476  */
0477 bool isInsideComment(CXTranslationUnit unit, CXFile file, const KTextEditor::Cursor& position)
0478 {
0479     if (!position.isValid()) {
0480         return false;
0481     }
0482 
0483     // TODO: This may get very slow for a large TU, investigate if we can improve this function
0484     auto begin = clang_getLocation(unit, file, 1, 1);
0485     auto end = clang_getLocation(unit, file, position.line() + 1, position.column() + 1);
0486     CXSourceRange range = clang_getRange(begin, end);
0487 
0488     // tokenize the whole range from the start until 'position'
0489     // if we detect a comment token at this position, return true
0490     const ClangTokens tokens(unit, range);
0491     for (CXToken token : tokens) {
0492         CXTokenKind tokenKind = clang_getTokenKind(token);
0493         if (tokenKind != CXToken_Comment) {
0494             continue;
0495         }
0496 
0497         auto range = ClangRange(clang_getTokenExtent(unit, token));
0498         if (range.toRange().contains(position)) {
0499             return true;
0500         }
0501     }
0502     return false;
0503 }
0504 
0505 /**
0506  * @return whether @p result is a builtin provided by KDevelop's automatically included GCC
0507  *         compatibility header, which libclang does not recognize as a builtin.
0508  */
0509 bool isGccCompatibilityBuiltin(CXCompletionResult result)
0510 {
0511     // The "#pragma clang system_header" line in plugins/clang/duchain/gccCompatibility/additional_floating_types.h
0512     // suppresses __KDevelopClangGccCompat Namespace and __float80 TypedefDecl completions, but not _FloatX TypedefDecl
0513     // completions. That's because clang_codeCompleteAt() filters out identifiers declared in a system header that start
0514     // with two underscores, but not an underscore followed by a capital letter, possibly due to a libclang bug.
0515     // The included typedefs substitute for separate floating-point types provided by GCC. Thus they are similar to
0516     // other builtin completions, such as int and double. For this reason, they are marked as builtins rather than
0517     // simply removed. The missing __float80 builtin completion shouldn't be a problem in practice, so we keep the
0518     // "#pragma clang system_header" line to make this function slightly simpler and more efficient.
0519 
0520     if (result.CursorKind != CXCursor_TypedefDecl || clang_getNumCompletionChunks(result.CompletionString) != 1
0521         || clang_getCompletionChunkKind(result.CompletionString, 0) != CXCompletionChunk_TypedText) {
0522         return false;
0523     }
0524 
0525     const ClangString clangString{clang_getCompletionChunkText(result.CompletionString, 0)};
0526     const auto text = clangString.c_str();
0527     const auto textSize = std::strlen(text);
0528 
0529     if (textSize != 8 && textSize != 9) {
0530         return false;
0531     }
0532 
0533     constexpr auto prefixSize = 6;
0534     if (!std::equal(text, text + prefixSize, "_Float")) {
0535         return false;
0536     }
0537     // TODO: explicitly capture [text, textSize] once building KDevelop with Visual Studio 2019 is no longer supported.
0538     const auto suffixEquals = [=](const char* suffix) {
0539         Q_ASSERT(std::strlen(suffix) == textSize - prefixSize);
0540         return std::equal(text + prefixSize, text + textSize, suffix);
0541     };
0542     if (textSize == 8) {
0543         return suffixEquals("32") || suffixEquals("64");
0544     } else {
0545         return suffixEquals("32x") || suffixEquals("64x") || suffixEquals("128");
0546     }
0547 }
0548 
0549 QString& elideStringRight(QString& str, int length)
0550 {
0551     if (str.size() > length + 3) {
0552         return str.replace(length, str.size() - length, QStringLiteral("..."));
0553     }
0554     return str;
0555 }
0556 
0557 constexpr int maxBestMatchCompletionPriority = CCP_SuperCompletion;
0558 
0559 /**
0560  * @return Value suited for @ref CodeCompletionModel::MatchQuality in the range [1, 10] (the higher the better)
0561  *
0562  * See https://clang.llvm.org/doxygen/CodeCompleteConsumer_8h_source.html for list of priorities
0563  * They (currently) are in the range [0, 80] (the lower, the better). Nevertheless, we are only setting priority
0564  * until maxBestMatchCompletionPriority (20), so we better build the value around it.
0565  */
0566 int matchQualityFromBestMatchCompletionPriority(int completionPriority)
0567 {
0568     Q_ASSERT(completionPriority >= 0);
0569     Q_ASSERT(completionPriority <= maxBestMatchCompletionPriority);
0570 
0571     constexpr int maxMatchQuality = 10;
0572     auto matchQuality = maxMatchQuality - maxMatchQuality * completionPriority / maxBestMatchCompletionPriority;
0573     Q_ASSERT(matchQuality >= 0);
0574 
0575     // KTextEditor considers a completion with matchQuality == 0 not suitable.
0576     // DeclarationItem::data() considers matchQuality == 0 invalid and does not return it.
0577     // Avoid zero special case by increasing matchQuality 0 to 1.
0578     constexpr int minSuitableMatchQuality = 1;
0579     matchQuality = std::max(minSuitableMatchQuality, matchQuality);
0580 
0581     Q_ASSERT(matchQuality >= minSuitableMatchQuality);
0582     Q_ASSERT(matchQuality <= maxMatchQuality);
0583     return matchQuality;
0584 }
0585 
0586 int adjustPriorityForType(const AbstractType::Ptr& type, int completionPriority)
0587 {
0588     const auto modifier = 4;
0589     if (type) {
0590         const auto whichType = type->whichType();
0591         if (whichType == AbstractType::TypePointer || whichType == AbstractType::TypeReference) {
0592             // Clang considers all pointers as similar, this is not what we want.
0593             completionPriority += modifier;
0594         } else if (whichType == AbstractType::TypeStructure) {
0595             // Clang considers all classes as similar too...
0596             completionPriority += modifier;
0597         } else if (whichType == AbstractType::TypeDelayed) {
0598             completionPriority += modifier;
0599         } else if (whichType == AbstractType::TypeAlias) {
0600             auto aliasedType = type.staticCast<TypeAliasType>();
0601             return adjustPriorityForType(aliasedType->type(), completionPriority);
0602         } else if (whichType == AbstractType::TypeFunction) {
0603             auto functionType = type.staticCast<FunctionType>();
0604             return adjustPriorityForType(functionType->returnType(), completionPriority);
0605         }
0606     } else {
0607         completionPriority += modifier;
0608     }
0609 
0610     return completionPriority;
0611 }
0612 
0613 /// Adjusts priority for the @p decl
0614 int adjustPriorityForDeclaration(Declaration* decl, int completionPriority)
0615 {
0616     if(completionPriority < CCP_LocalDeclarationSimiliar || completionPriority > CCP_SuperCompletion){
0617         return completionPriority;
0618     }
0619 
0620     return adjustPriorityForType(decl->abstractType(), completionPriority);
0621 }
0622 
0623 /**
0624  * @return Whether the declaration represented by identifier @p identifier qualifies as completion result
0625  *
0626  * For example, we don't want to offer SomeClass::SomeClass as completion item to the user
0627  * (otherwise we'd end up generating code such as 's.SomeClass();')
0628  */
0629 bool isValidCompletionIdentifier(const QualifiedIdentifier& identifier)
0630 {
0631     const int count = identifier.count();
0632     if (identifier.count() < 2) {
0633         return true;
0634     }
0635 
0636     const Identifier scope = identifier.at(count-2);
0637     const Identifier id = identifier.last();
0638     if (scope == id) {
0639         return false; // is constructor
0640     }
0641     const QString idString = id.toString();
0642     if (idString.startsWith(QLatin1Char('~')) && scope.toString() == idString.midRef(1)) {
0643         return false; // is destructor
0644     }
0645     return true;
0646 }
0647 
0648 /**
0649  * @return Whether the declaration represented by identifier @p identifier qualifies as "special" completion result
0650  *
0651  * "Special" completion results are items that are likely not regularly used.
0652  *
0653  * Examples:
0654  * - 'SomeClass::operator=(const SomeClass&)'
0655  */
0656 bool isValidSpecialCompletionIdentifier(const QualifiedIdentifier& identifier)
0657 {
0658     if (identifier.count() < 2) {
0659         return false;
0660     }
0661 
0662     const Identifier id = identifier.last();
0663     const QString idString = id.toString();
0664     if (idString.startsWith(QLatin1String("operator="))) {
0665         return true; // is assignment operator
0666     }
0667     return false;
0668 }
0669 
0670 Declaration* findDeclaration(const QualifiedIdentifier& qid, const DUContextPointer& ctx, const CursorInRevision& position, QSet<Declaration*>& handled)
0671 {
0672     Declaration* ret = nullptr;
0673     const auto& importedContexts = ctx->topContext()->importedParentContexts();
0674     auto visitor = [&](const IndexedDeclaration& indexedDeclaration) {
0675         // if the context is not included, then this match is not correct for our consideration
0676         // this fixes issues where we used to include matches from files that did not have
0677         // anything to do with the current TU, e.g. the main from a different file or stuff like that
0678         // it also reduces the chance of us picking up a function of the same name from somewhere else
0679         // also, this makes sure the context has the correct language and we don't get confused by stuff
0680         // from other language plugins
0681         if (std::none_of(importedContexts.begin(), importedContexts.end(),
0682                          [indexedDeclaration](const DUContext::Import& import) {
0683                              return import.topContextIndex() == indexedDeclaration.indexedTopContext().index();
0684                          })) {
0685             return PersistentSymbolTable::VisitorState::Continue;
0686         }
0687 
0688         auto declaration = indexedDeclaration.declaration();
0689         if (!declaration) {
0690             // Mitigate problems such as: Cannot load a top-context from file "/home/kfunk/.cache/kdevduchain/kdevelop-{foo}/topcontexts/6085"
0691             //  - the required language-support for handling ID 55 is probably not loaded
0692             qCWarning(KDEV_CLANG) << "Detected an invalid declaration for" << qid;
0693 
0694             return PersistentSymbolTable::VisitorState::Continue;
0695         }
0696 
0697         if (declaration->kind() == Declaration::Instance && !declaration->isFunctionDeclaration()) {
0698             return PersistentSymbolTable::VisitorState::Break;
0699         }
0700         if (!handled.contains(declaration)) {
0701             handled.insert(declaration);
0702 
0703             ret = declaration;
0704             return PersistentSymbolTable::VisitorState::Break;
0705         }
0706 
0707         return PersistentSymbolTable::VisitorState::Continue;
0708     };
0709     PersistentSymbolTable::self().visitDeclarations(qid, visitor);
0710     if (ret) {
0711         return ret;
0712     }
0713 
0714     const auto foundDeclarations = ctx->findDeclarations(qid, position);
0715     for (auto dec : foundDeclarations) {
0716         if (!handled.contains(dec)) {
0717             handled.insert(dec);
0718             return dec;
0719         }
0720     }
0721 
0722     return nullptr;
0723 }
0724 
0725 /// If any parent of this context is a class, the closest class declaration is returned, nullptr otherwise
0726 Declaration* classDeclarationForContext(const DUContextPointer& context, const CursorInRevision& position)
0727 {
0728     auto parent = context;
0729     while (parent) {
0730         if (parent->type() == DUContext::Class) {
0731             break;
0732         }
0733 
0734         if (auto owner = parent->owner()) {
0735             // Work-around for out-of-line methods. They have Helper context instead of Class context
0736             if (owner->context() && owner->context()->type() == DUContext::Helper) {
0737                 auto qid = owner->qualifiedIdentifier();
0738                 qid.pop();
0739 
0740                 QSet<Declaration*> tmp;
0741                 auto decl = findDeclaration(qid, context, position, tmp);
0742 
0743                 if (decl && decl->internalContext() && decl->internalContext()->type() == DUContext::Class) {
0744                     parent = decl->internalContext();
0745                     break;
0746                 }
0747             }
0748         }
0749         parent = parent->parentContext();
0750     }
0751 
0752     return parent ? parent->owner() : nullptr;
0753 }
0754 
0755 class LookAheadItemMatcher
0756 {
0757 public:
0758     explicit LookAheadItemMatcher(const TopDUContextPointer& ctx)
0759         : m_topContext(ctx)
0760         , m_enabled(ClangSettingsManager::self()->codeCompletionSettings().lookAhead)
0761     {}
0762 
0763     /// Adds all local declarations for @p declaration into possible look-ahead items.
0764     void addDeclarations(Declaration* declaration)
0765     {
0766         if (!m_enabled) {
0767             return;
0768         }
0769 
0770         if (declaration->kind() != Declaration::Instance) {
0771             return;
0772         }
0773 
0774         auto type = typeForDeclaration(declaration);
0775         auto identifiedType = dynamic_cast<const IdentifiedType*>(type.data());
0776         if (!identifiedType) {
0777             return;
0778         }
0779 
0780         addDeclarationsForType(identifiedType, declaration);
0781     }
0782 
0783     /// Add type for matching. This type'll be used for filtering look-ahead items
0784     /// Only items with @p type will be returned through @sa matchedItems
0785     /// @param matchQuality @p type's match quality
0786     void addMatchedType(const IndexedType& type, int matchQuality)
0787     {
0788         Q_ASSERT_X(matchQuality > 0, Q_FUNC_INFO, "The completion must be suitable.");
0789         if (!type.isValid()) {
0790             return;
0791         }
0792         auto& lookAheadMatchQuality = matchedTypeToMatchQuality[type];
0793         // Use the highest among match qualities associated with the type, because currently
0794         // supported look-ahead completions are as useful as CCP_LocalDeclaration, and the
0795         // corresponding match quality is unlikely to be lower than @p matchQuality.
0796         // The following statement correctly assigns @p matchQuality if @p type was just inserted into
0797         // matchedTypeToMatchQuality, because then lookAheadMatchQuality == int{} == 0 < matchQuality.
0798         lookAheadMatchQuality = std::max(lookAheadMatchQuality, matchQuality);
0799     }
0800 
0801     /// @return look-ahead items that math given types. @sa addMatchedType
0802     QList<CompletionTreeItemPointer> matchedItems() const
0803     {
0804         QList<CompletionTreeItemPointer> lookAheadItems;
0805         for (const auto& pair: qAsConst(possibleLookAheadDeclarations)) {
0806             auto decl = pair.first;
0807             if (const auto matchQuality = matchedTypeToMatchQuality.value(decl->indexedType())) {
0808                 auto parent = pair.second;
0809                 const QLatin1String access = (parent->abstractType()->whichType() == AbstractType::TypePointer)
0810                                  ? QLatin1String("->") : QLatin1String(".");
0811                 const QString text = parent->identifier().toString() + access + decl->identifier().toString();
0812                 auto item = new DeclarationItem(decl, text, {}, text);
0813                 item->setMatchQuality(matchQuality);
0814                 lookAheadItems.append(CompletionTreeItemPointer(item));
0815             }
0816         }
0817 
0818         return lookAheadItems;
0819     }
0820 
0821 private:
0822     AbstractType::Ptr typeForDeclaration(const Declaration* decl)
0823     {
0824         return TypeUtils::targetType(decl->abstractType(), m_topContext.data());
0825     }
0826 
0827     void addDeclarationsForType(const IdentifiedType* identifiedType, Declaration* declaration)
0828     {
0829         if (auto typeDecl = identifiedType->declaration(m_topContext.data())) {
0830             if (dynamic_cast<ClassDeclaration*>(typeDecl->logicalDeclaration(m_topContext.data()))) {
0831                 if (!typeDecl->internalContext()) {
0832                     return;
0833                 }
0834 
0835                 const auto& localDeclarations = typeDecl->internalContext()->localDeclarations();
0836                 for (auto localDecl : localDeclarations) {
0837                     if(localDecl->identifier().isEmpty()){
0838                         continue;
0839                     }
0840 
0841                     if(auto classMember = dynamic_cast<ClassMemberDeclaration*>(localDecl)){
0842                         // TODO: Also add protected/private members if completion is inside this class context.
0843                         if(classMember->accessPolicy() != Declaration::Public){
0844                             continue;
0845                         }
0846                     }
0847 
0848                     if (!localDecl->abstractType()) {
0849                         continue;
0850                     }
0851 
0852                     if (localDecl->abstractType()->whichType() == AbstractType::TypeIntegral) {
0853                         if (auto integralType = declaration->abstractType().dynamicCast<IntegralType>()) {
0854                             if (integralType->dataType() == IntegralType::TypeVoid) {
0855                                 continue;
0856                             }
0857                         }
0858                     }
0859 
0860                     possibleLookAheadDeclarations.insert({localDecl, declaration});
0861                 }
0862             }
0863         }
0864     }
0865 
0866     // Declaration and it's context
0867     using DeclarationContext = QPair<Declaration*, Declaration*>;
0868 
0869     /// Types of declarations that look-ahead completion items can have and their match qualities
0870     QHash<IndexedType, int> matchedTypeToMatchQuality;
0871 
0872     // List of declarations that can be added to the Look Ahead group
0873     // Second declaration represents context
0874     QSet<DeclarationContext> possibleLookAheadDeclarations;
0875 
0876     TopDUContextPointer m_topContext;
0877 
0878     bool m_enabled;
0879 };
0880 
0881 struct MemberAccessReplacer : public QObject
0882 {
0883     Q_OBJECT
0884 
0885 public:
0886     enum Type {
0887         None,
0888         DotToArrow,
0889         ArrowToDot
0890     };
0891     Q_ENUM(Type)
0892 
0893 public Q_SLOTS:
0894     void replaceCurrentAccess(MemberAccessReplacer::Type type)
0895     {
0896         if (auto document = ICore::self()->documentController()->activeDocument()) {
0897             if (auto textDocument = document->textDocument()) {
0898                 auto activeView = document->activeTextView();
0899                 if (!activeView) {
0900                     return;
0901                 }
0902 
0903                 auto cursor = activeView->cursorPosition();
0904 
0905                 QString oldAccess, newAccess;
0906                 if (type == ArrowToDot) {
0907                     oldAccess = QStringLiteral("->");
0908                     newAccess = QStringLiteral(".");
0909                 } else {
0910                     oldAccess = QStringLiteral(".");
0911                     newAccess = QStringLiteral("->");
0912                 }
0913 
0914                 auto oldRange = KTextEditor::Range(cursor - KTextEditor::Cursor(0, oldAccess.length()), cursor);
0915 
0916                 // This code needed for testReplaceMemberAccess test
0917                 // Maybe we should do a similar thing for '->' to '.' direction, but this is not so important
0918                 while (textDocument->text(oldRange) == QLatin1String(" ") && oldRange.start().column() >= 0) {
0919                     oldRange = KTextEditor::Range({oldRange.start().line(), oldRange.start().column() - 1},
0920                                                   {oldRange.end().line(), oldRange.end().column() - 1});
0921                 }
0922 
0923                 if (oldRange.start().column() >= 0 && textDocument->text(oldRange) == oldAccess) {
0924                     textDocument->replaceText(oldRange, newAccess);
0925                 }
0926             }
0927         }
0928     }
0929 };
0930 static MemberAccessReplacer s_memberAccessReplacer;
0931 
0932 bool areAllResultsOverloadCandidates(const CXCodeCompleteResults& results)
0933 {
0934 #if CINDEX_VERSION_MINOR >= 30
0935     return std::all_of(results.Results, results.Results + results.NumResults, [](const CXCompletionResult& result) {
0936         return result.CursorKind == CXCursor_OverloadCandidate;
0937     });
0938 #else
0939     // CXCursor_OverloadCandidate is unavailable in this version, so we return true only if there are no results.
0940     return results.NumResults == 0;
0941 #endif
0942 }
0943 } // namespace
0944 
0945 ClangCodeCompletionContext::ClangCodeCompletionContext(const DUContextPointer& context,
0946                                                        const ParseSessionData::Ptr& sessionData,
0947                                                        const QUrl& url,
0948                                                        const KTextEditor::Cursor& position,
0949                                                        const QString& text,
0950                                                        const QString& followingText
0951                                                       )
0952     : CodeCompletionContext(context, text + followingText, CursorInRevision::castFromSimpleCursor(position), 0)
0953     , m_results(nullptr, clang_disposeCodeCompleteResults)
0954     , m_parseSessionData(sessionData)
0955 {
0956     qRegisterMetaType<MemberAccessReplacer::Type>();
0957     const QByteArray file = url.toLocalFile().toUtf8();
0958     ParseSession session(m_parseSessionData);
0959 
0960     QVector<UnsavedFile> otherUnsavedFiles;
0961     {
0962         ForegroundLock lock;
0963         otherUnsavedFiles = ClangUtils::unsavedFiles();
0964     }
0965     QVector<CXUnsavedFile> allUnsaved;
0966 
0967     {
0968         const unsigned int completeOptions = clang_defaultCodeCompleteOptions();
0969 
0970         CXUnsavedFile unsaved;
0971         unsaved.Filename = file.constData();
0972         const QByteArray content = m_text.toUtf8();
0973         unsaved.Contents = content.constData();
0974         unsaved.Length = content.size();
0975 
0976         allUnsaved.reserve(otherUnsavedFiles.size() + 1);
0977         for (const auto& f : qAsConst(otherUnsavedFiles)) {
0978             allUnsaved.append(f.toClangApi());
0979         }
0980         allUnsaved.append(unsaved);
0981 
0982         m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
0983                         position.line() + 1, position.column() + 1,
0984                         allUnsaved.data(), allUnsaved.size(),
0985                         completeOptions));
0986 
0987         if (!m_results) {
0988             qCWarning(KDEV_CLANG) << "Something went wrong during 'clang_codeCompleteAt' for file" << file;
0989             return;
0990         }
0991 
0992         auto numDiagnostics = clang_codeCompleteGetNumDiagnostics(m_results.get());
0993         for (uint i = 0; i < numDiagnostics; i++) {
0994             auto diagnostic = clang_codeCompleteGetDiagnostic(m_results.get(), i);
0995             auto diagnosticType = ClangDiagnosticEvaluator::diagnosticType(diagnostic);
0996             clang_disposeDiagnostic(diagnostic);
0997             if (diagnosticType == ClangDiagnosticEvaluator::ReplaceWithArrowProblem || diagnosticType == ClangDiagnosticEvaluator::ReplaceWithDotProblem) {
0998                 MemberAccessReplacer::Type replacementType;
0999                 if (diagnosticType == ClangDiagnosticEvaluator::ReplaceWithDotProblem) {
1000                     replacementType = MemberAccessReplacer::ArrowToDot;
1001                 } else {
1002                     replacementType = MemberAccessReplacer::DotToArrow;
1003                 }
1004 
1005                 QMetaObject::invokeMethod(&s_memberAccessReplacer, "replaceCurrentAccess", Qt::QueuedConnection,
1006                                           Q_ARG(MemberAccessReplacer::Type, replacementType));
1007 
1008                 m_valid = false;
1009                 return;
1010             }
1011         }
1012 
1013         auto addMacros = ClangSettingsManager::self()->codeCompletionSettings().macros;
1014         if (!addMacros) {
1015             m_filters |= NoMacros;
1016         }
1017     }
1018 
1019     if (!m_results->NumResults) {
1020         const auto trimmedText = text.trimmed();
1021         if (trimmedText.endsWith(QLatin1Char('.'))) {
1022             // TODO: This shouldn't be needed if Clang provided diagnostic.
1023             // But it doesn't always do it, so let's try to manually determine whether '.' is used instead of '->'
1024             m_text = trimmedText.leftRef(trimmedText.size() - 1) + QLatin1String("->");
1025 
1026             CXUnsavedFile unsaved;
1027             unsaved.Filename = file.constData();
1028             const QByteArray content = m_text.toUtf8();
1029             unsaved.Contents = content.constData();
1030             unsaved.Length = content.size();
1031             allUnsaved[allUnsaved.size() - 1] = unsaved;
1032 
1033             m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
1034                                                  position.line() + 1, position.column() + 1 + 1,
1035                                                  allUnsaved.data(), allUnsaved.size(),
1036                                                  clang_defaultCodeCompleteOptions()));
1037 
1038             m_valid = false;
1039 
1040             // Overload candidate completions do not make sense after '->', but Clang proposes such a completion after
1041             // a digit sometimes. Ignore the overload candidate completions to prevent a wrong '.' to '->' replacement.
1042             if (!m_results || areAllResultsOverloadCandidates(*m_results)) {
1043                 return; // do not replace '.' with '->', because there are no useful completions after '->'
1044             }
1045 
1046             const auto diagnostic = clang_codeCompleteGetDiagnostic(m_results.get(), 0);
1047             const ClangString str(clang_getDiagnosticCategoryText(diagnostic));
1048 
1049             // This is unfortunately not documented anywhere in clang
1050             const bool isParseIssue = (qstrcmp(str.c_str(), "Parse Issue") == 0);
1051             clang_disposeDiagnostic(diagnostic);
1052 
1053             // Do not perform subsitution if we get a parsing issue with the ->
1054             if (isParseIssue) {
1055                 return;
1056             }
1057 
1058             QMetaObject::invokeMethod(&s_memberAccessReplacer, "replaceCurrentAccess", Qt::QueuedConnection,
1059                                       Q_ARG(MemberAccessReplacer::Type, MemberAccessReplacer::DotToArrow));
1060 
1061             return;
1062         }
1063     }
1064 
1065     // check 'isValidPosition' after parsing the new content
1066     auto clangFile = session.file(file);
1067     if (!isValidPosition(session.unit(), clangFile)) {
1068         m_valid = false;
1069         return;
1070     }
1071 
1072     m_completionHelper.computeCompletions(session, clangFile, position);
1073 }
1074 
1075 ClangCodeCompletionContext::~ClangCodeCompletionContext()
1076 {
1077 }
1078 
1079 bool ClangCodeCompletionContext::isValidPosition(CXTranslationUnit unit, CXFile file) const
1080 {
1081     if (isInsideComment(unit, file, m_position.castToSimpleCursor())) {
1082         clangDebug() << "Invalid completion context: Inside comment";
1083         return false;
1084     }
1085     return true;
1086 }
1087 
1088 QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(bool& abort, bool /*fullCompletion*/)
1089 {
1090     if (!m_valid || !m_duContext || !m_results) {
1091         return {};
1092     }
1093 
1094     const auto ctx = DUContextPointer(m_duContext->findContextAt(m_position));
1095 
1096     /// Normal completion items, such as 'void Foo::foo()'
1097     QList<CompletionTreeItemPointer> items;
1098     /// Stuff like 'Foo& Foo::operator=(const Foo&)', etc. Not regularly used by our users.
1099     QList<CompletionTreeItemPointer> specialItems;
1100     /// Macros from the current context
1101     QList<CompletionTreeItemPointer> macros;
1102     /// Builtins reported by Clang
1103     QList<CompletionTreeItemPointer> builtin;
1104 
1105     // two sets of handled declarations to prevent duplicates and make sure we show
1106     // all available overloads
1107     QSet<Declaration*> handled;
1108     // this is only used for the CXCursor_OverloadCandidate completion items
1109     QSet<Declaration*> overloadsHandled;
1110 
1111     LookAheadItemMatcher lookAheadMatcher(TopDUContextPointer(ctx->topContext()));
1112 
1113     // If ctx is/inside the Class context, this represents that context.
1114     const auto currentClassContext = classDeclarationForContext(ctx, m_position);
1115 
1116     // HACK: try to build a fallback parent ID from the USR
1117     //       otherwise we won't identify typedefed anon structs correctly :(
1118     auto parentFromUSR = [this]() -> QString {
1119         const auto containerUSR = ClangString(clang_codeCompleteGetContainerUSR(m_results.get())).toString();
1120         const auto lastAt = containerUSR.lastIndexOf(QLatin1Char('@'));
1121         if (lastAt <= 0 || containerUSR[lastAt - 1] != QLatin1Char('A')) // we use this hack only for _A_non stuff
1122             return {};
1123 
1124         return containerUSR.mid(lastAt + 1);
1125     };
1126     const auto fallbackParentFromUSR = parentFromUSR();
1127 
1128     clangDebug() << "Clang found" << m_results->NumResults << "completion results";
1129 
1130     for (uint i = 0; i < m_results->NumResults; ++i) {
1131         if (abort) {
1132             return {};
1133         }
1134 
1135         auto result = m_results->Results[i];
1136         #if CINDEX_VERSION_MINOR >= 30
1137         const bool isOverloadCandidate = result.CursorKind == CXCursor_OverloadCandidate;
1138         #else
1139         const bool isOverloadCandidate = false;
1140         #endif
1141 
1142         const auto availability = clang_getCompletionAvailability(result.CompletionString);
1143         if (availability == CXAvailability_NotAvailable) {
1144             continue;
1145         }
1146 
1147         const bool isMacroDefinition = result.CursorKind == CXCursor_MacroDefinition;
1148         if (isMacroDefinition && m_filters & NoMacros) {
1149             continue;
1150         }
1151 
1152         const bool isBuiltin = result.CursorKind == CXCursor_NotImplemented || isGccCompatibilityBuiltin(result);
1153         if (isBuiltin && m_filters & NoBuiltins) {
1154             continue;
1155         }
1156 
1157         const bool isDeclaration = !isMacroDefinition && !isBuiltin;
1158         if (isDeclaration && m_filters & NoDeclarations) {
1159             continue;
1160         }
1161 
1162         if (availability == CXAvailability_NotAccessible && (!isDeclaration || !currentClassContext)) {
1163             continue;
1164         }
1165 
1166         // the string that would be needed to type, usually the identifier of something. Also we use it as name for code completion declaration items.
1167         QString typed;
1168         // the return type of a function e.g.
1169         QString resultType;
1170         // the replacement text when an item gets executed
1171         QString replacement;
1172 
1173         QString arguments;
1174 
1175         ArgumentHintItem::CurrentArgumentRange argumentRange;
1176         //BEGIN function signature parsing
1177         // nesting depth of parentheses
1178         int parenDepth = 0;
1179         enum FunctionSignatureState {
1180             // not yet inside the function signature
1181             Before,
1182             // any token is part of the function signature now
1183             Inside,
1184             // finished parsing the function signature
1185             After
1186         };
1187         // current state
1188         FunctionSignatureState signatureState = Before;
1189         //END function signature parsing
1190 
1191         const auto processChunks = [&](CXCompletionString completionString, const auto& self) -> void {
1192             const uint chunks = clang_getNumCompletionChunks(completionString);
1193             for (uint j = 0; j < chunks; ++j) {
1194                 const auto kind = clang_getCompletionChunkKind(completionString, j);
1195                 if (kind == CXCompletionChunk_Optional) {
1196                     completionString = clang_getCompletionChunkCompletionString(completionString, j);
1197                     if (completionString) {
1198                         self(completionString, self);
1199                     }
1200                     continue;
1201                 }
1202 
1203                 // 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.
1204                 if (isDeclaration && !typed.isEmpty()) {
1205                     // TODO: When parent context for CXCursor_OverloadCandidate is fixed remove this check
1206                     if (!isOverloadCandidate) {
1207                         break;
1208                     }
1209                 }
1210 
1211                 const QString string = ClangString(clang_getCompletionChunkText(completionString, j)).toString();
1212 
1213                 switch (kind) {
1214                 case CXCompletionChunk_TypedText:
1215                     typed = string;
1216                     replacement += string;
1217                     break;
1218                 case CXCompletionChunk_ResultType:
1219                     resultType = string;
1220                     continue;
1221                 case CXCompletionChunk_Placeholder:
1222                     if (signatureState == Inside) {
1223                         arguments += string;
1224                     }
1225                     continue;
1226                 case CXCompletionChunk_LeftParen:
1227                     if (signatureState == Before && !parenDepth) {
1228                         signatureState = Inside;
1229                     }
1230                     parenDepth++;
1231                     break;
1232                 case CXCompletionChunk_RightParen:
1233                     --parenDepth;
1234                     if (signatureState == Inside && !parenDepth) {
1235                         arguments += QLatin1Char(')');
1236                         signatureState = After;
1237                     }
1238                     break;
1239                 case CXCompletionChunk_Text:
1240                     if (isOverloadCandidate) {
1241                         typed += string;
1242                     }
1243                     else if (result.CursorKind == CXCursor_EnumConstantDecl) {
1244                         replacement += string;
1245                     }
1246                     else if (result.CursorKind == CXCursor_EnumConstantDecl) {
1247                         replacement += string;
1248                     }
1249                     break;
1250                 case CXCompletionChunk_CurrentParameter:
1251                     argumentRange.start = arguments.size();
1252                     argumentRange.end = string.size();
1253                     break;
1254                 default:
1255                     break;
1256                 }
1257                 if (signatureState == Inside) {
1258                     arguments += string;
1259                 }
1260             }
1261         };
1262 
1263         processChunks(result.CompletionString, processChunks);
1264 
1265         // we have our own implementation of an override helper
1266         // TODO: use the clang-provided one, if available
1267         if (typed.endsWith(QLatin1String(" override")))
1268             continue;
1269 
1270         // TODO: No closing paren if default parameters present
1271         if (isOverloadCandidate && !arguments.endsWith(QLatin1Char(')'))) {
1272             arguments += QLatin1Char(')');
1273         }
1274         // ellide text to the right for overly long result types (templates especially)
1275         elideStringRight(resultType, MAX_RETURN_TYPE_STRING_LENGTH);
1276 
1277         static const auto noIcon = QIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation,
1278                                                                 QStringLiteral("kdevelop/pics/namespace.png")));
1279 
1280         if (isDeclaration) {
1281             const Identifier id(typed);
1282             QualifiedIdentifier qid;
1283             auto parent = ClangString(clang_getCompletionParent(result.CompletionString, nullptr)).toString();
1284             if (parent.isEmpty() && !fallbackParentFromUSR.isEmpty()) {
1285                 parent = fallbackParentFromUSR;
1286             }
1287             if (!parent.isEmpty()) {
1288                 qid = QualifiedIdentifier(parent);
1289             }
1290             qid.push(id);
1291 
1292             if (!isValidCompletionIdentifier(qid)) {
1293                 continue;
1294             }
1295 
1296             if (isOverloadCandidate && resultType.isEmpty() && parent.isEmpty()) {
1297                 // workaround: find constructor calls for non-namespaced classes
1298                 // TODO: return the namespaced class as parent in libclang
1299                 qid.push(id);
1300             }
1301 
1302             auto found = findDeclaration(qid, ctx, m_position, isOverloadCandidate ? overloadsHandled : handled);
1303 
1304             CompletionTreeItemPointer item;
1305             if (found) {
1306                 // TODO: Bug in Clang: protected members from base classes not accessible in derived classes.
1307                 if (availability == CXAvailability_NotAccessible) {
1308                     if (auto cl = dynamic_cast<ClassMemberDeclaration*>(found)) {
1309                         if (cl->accessPolicy() != Declaration::Protected) {
1310                             continue;
1311                         }
1312 
1313                         auto declarationClassContext = classDeclarationForContext(DUContextPointer(found->context()), m_position);
1314 
1315                         uint steps = 10;
1316                         auto inheriters = DUChainUtils::inheriters(declarationClassContext, steps);
1317                         if(!inheriters.contains(currentClassContext)){
1318                             continue;
1319                         }
1320                     } else {
1321                         continue;
1322                     }
1323                 }
1324 
1325                 DeclarationItem* declarationItem = nullptr;
1326                 if (isOverloadCandidate) {
1327                     declarationItem = new ArgumentHintItem(found, resultType, typed, arguments, argumentRange);
1328                     declarationItem->setArgumentHintDepth(1);
1329                 } else {
1330                     declarationItem = new DeclarationItem(found, typed, resultType, replacement);
1331                 }
1332 
1333                 const auto completionPriority =
1334                     adjustPriorityForDeclaration(found, clang_getCompletionPriority(result.CompletionString));
1335                 const bool bestMatch = completionPriority <= maxBestMatchCompletionPriority;
1336 
1337                 // don't set best match property for reserved identifiers, also prefer declarations from current file
1338                 const bool isReserved = found->indexedIdentifier().identifier().isReserved();
1339                 if (bestMatch && !isReserved) {
1340                     const auto matchQuality = matchQualityFromBestMatchCompletionPriority(completionPriority);
1341                     declarationItem->setMatchQuality(matchQuality);
1342 
1343                     // TODO: LibClang missing API to determine expected code completion type.
1344                     if (auto functionType = found->type<FunctionType>()) {
1345                         lookAheadMatcher.addMatchedType(IndexedType(functionType->returnType()), matchQuality);
1346                     }
1347                     lookAheadMatcher.addMatchedType(found->indexedType(), matchQuality);
1348                 } else {
1349                     declarationItem->setInheritanceDepth(completionPriority);
1350 
1351                     lookAheadMatcher.addDeclarations(found);
1352                 }
1353                 if (isReserved) {
1354                     declarationItem->markAsUnimportant();
1355                 }
1356 
1357                 item = declarationItem;
1358             } else {
1359                 if (isOverloadCandidate) {
1360                     // TODO: No parent context for CXCursor_OverloadCandidate items, hence qid is broken -> no declaration found
1361                     auto ahi = new ArgumentHintItem({}, resultType, typed, arguments, argumentRange);
1362                     ahi->setArgumentHintDepth(1);
1363                     item = ahi;
1364                 } else {
1365                     // still, let's trust that Clang found something useful and put it into the completion result list
1366                     clangDebug() << "Could not find declaration for" << qid;
1367                     auto instance = new SimpleItem(typed + arguments, resultType, replacement, noIcon);
1368                     instance->markAsUnimportant();
1369                     item = CompletionTreeItemPointer(instance);
1370                 }
1371             }
1372 
1373             if (isValidSpecialCompletionIdentifier(qid)) {
1374                 // If it's a special completion identifier e.g. "operator=(const&)" and we don't have a declaration for it, don't add it into completion list, as this item is completely useless and pollutes the test case.
1375                 // This happens e.g. for "class A{}; a.|".  At | we have "operator=(const A&)" as a special completion identifier without a declaration.
1376                 if(item->declaration()){
1377                     specialItems.append(item);
1378                 }
1379             } else {
1380                 items.append(item);
1381             }
1382             continue;
1383         }
1384 
1385         if (isMacroDefinition) {
1386             // TODO: grouping of macros and built-in stuff
1387             const auto text = QString(typed + arguments);
1388             auto instance = new SimpleItem(text, resultType, replacement, noIcon);
1389             auto item = CompletionTreeItemPointer(instance);
1390             if ( text.startsWith(QLatin1Char('_')) ) {
1391                 instance->markAsUnimportant();
1392             }
1393             macros.append(item);
1394         } else if (isBuiltin) {
1395             auto instance = new SimpleItem(typed, resultType, replacement, noIcon);
1396             auto item = CompletionTreeItemPointer(instance);
1397             builtin.append(item);
1398         } else {
1399             Q_UNREACHABLE();
1400         }
1401     }
1402 
1403     if (abort) {
1404         return {};
1405     }
1406 
1407     addImplementationHelperItems();
1408     addOverwritableItems();
1409 
1410     eventuallyAddGroup(i18n("Special"), 700, specialItems);
1411     eventuallyAddGroup(i18n("Look-ahead Matches"), 800, lookAheadMatcher.matchedItems());
1412     eventuallyAddGroup(i18n("Builtin"), 900, builtin);
1413     eventuallyAddGroup(i18n("Macros"), 1000, macros);
1414     return items;
1415 }
1416 
1417 void ClangCodeCompletionContext::eventuallyAddGroup(const QString& name, int priority,
1418                                                     const QList<CompletionTreeItemPointer>& items)
1419 {
1420     if (items.isEmpty()) {
1421         return;
1422     }
1423 
1424     auto* node = new CompletionCustomGroupNode(name, priority);
1425     node->appendChildren(items);
1426     m_ungrouped << CompletionTreeElementPointer(node);
1427 }
1428 
1429 void ClangCodeCompletionContext::addOverwritableItems()
1430 {
1431     const auto overrideList = m_completionHelper.overrides();
1432     if (overrideList.isEmpty()) {
1433         return;
1434     }
1435 
1436     QList<CompletionTreeItemPointer> overrides;
1437     QList<CompletionTreeItemPointer> overridesAbstract;
1438     for (const auto& info : overrideList) {
1439         QStringList params;
1440         params.reserve(info.params.size());
1441         for (const auto& param : info.params) {
1442             params << param.type + QLatin1Char(' ') + param.id;
1443         }
1444         QString nameAndParams = info.name + QLatin1Char('(') + params.join(QLatin1String(", ")) + QLatin1Char(')');
1445         if(info.isConst)
1446             nameAndParams = nameAndParams + QLatin1String(" const");
1447         if(info.isPureVirtual)
1448             nameAndParams = nameAndParams + QLatin1String(" = 0");
1449 
1450         auto item = CompletionTreeItemPointer(new OverrideItem(nameAndParams, info.returnType));
1451         if (info.isPureVirtual)
1452             overridesAbstract << item;
1453         else
1454             overrides << item;
1455     }
1456     eventuallyAddGroup(i18n("Abstract Override"), 0, overridesAbstract);
1457     eventuallyAddGroup(i18n("Virtual Override"), 0, overrides);
1458 }
1459 
1460 void ClangCodeCompletionContext::addImplementationHelperItems()
1461 {
1462     const auto implementsList = m_completionHelper.implements();
1463     if (implementsList.isEmpty()) {
1464         return;
1465     }
1466 
1467     QList<CompletionTreeItemPointer> implements;
1468     implements.reserve(implementsList.size());
1469     for (const auto& info : implementsList) {
1470         implements << CompletionTreeItemPointer(new ImplementsItem(info));
1471     }
1472     eventuallyAddGroup(i18n("Implement Function"), 0, implements);
1473 }
1474 
1475 
1476 QList<CompletionTreeElementPointer> ClangCodeCompletionContext::ungroupedElements()
1477 {
1478     return m_ungrouped;
1479 }
1480 
1481 ClangCodeCompletionContext::ContextFilters ClangCodeCompletionContext::filters() const
1482 {
1483     return m_filters;
1484 }
1485 
1486 void ClangCodeCompletionContext::setFilters(const ClangCodeCompletionContext::ContextFilters& filters)
1487 {
1488     m_filters = filters;
1489 }
1490 
1491 #include "context.moc"