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"