1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "cppcompletionassist.h"
27 
28 #include "builtineditordocumentparser.h"
29 #include "cppdoxygen.h"
30 #include "cppmodelmanager.h"
31 #include "cpptoolsconstants.h"
32 #include "cpptoolsreuse.h"
33 #include "editordocumenthandle.h"
34 
35 #include <coreplugin/icore.h>
36 #include <cppeditor/cppeditorconstants.h>
37 #include <texteditor/codeassist/assistproposalitem.h>
38 #include <texteditor/codeassist/genericproposal.h>
39 #include <texteditor/codeassist/ifunctionhintproposalmodel.h>
40 #include <texteditor/codeassist/functionhintproposal.h>
41 #include <texteditor/snippets/snippet.h>
42 #include <texteditor/texteditorsettings.h>
43 #include <texteditor/completionsettings.h>
44 
45 #include <utils/textutils.h>
46 #include <utils/mimetypes/mimedatabase.h>
47 #include <utils/qtcassert.h>
48 
49 #include <cplusplus/BackwardsScanner.h>
50 #include <cplusplus/CppRewriter.h>
51 #include <cplusplus/ExpressionUnderCursor.h>
52 #include <cplusplus/MatchingText.h>
53 #include <cplusplus/Overview.h>
54 #include <cplusplus/ResolveExpression.h>
55 
56 #include <QDirIterator>
57 #include <QLatin1String>
58 #include <QTextCursor>
59 #include <QTextDocument>
60 #include <QIcon>
61 
62 using namespace CPlusPlus;
63 using namespace CppEditor;
64 using namespace CppTools;
65 using namespace CppTools::Internal;
66 using namespace TextEditor;
67 
68 namespace CppTools {
69 namespace Internal {
70 
71 struct CompleteFunctionDeclaration
72 {
CompleteFunctionDeclarationCppTools::Internal::CompleteFunctionDeclaration73     explicit CompleteFunctionDeclaration(Function *f = nullptr)
74         : function(f)
75     {}
76 
77     Function *function;
78 };
79 
80 // ---------------------
81 // CppAssistProposalItem
82 // ---------------------
83 class CppAssistProposalItem final : public AssistProposalItem
84 {
85 public:
86     ~CppAssistProposalItem() noexcept override = default;
87     bool prematurelyApplies(const QChar &c) const override;
88     void applyContextualContent(TextDocumentManipulatorInterface &manipulator, int basePosition) const override;
89 
isOverloaded() const90     bool isOverloaded() const { return m_isOverloaded; }
markAsOverloaded()91     void markAsOverloaded() { m_isOverloaded = true; }
keepCompletionOperator(unsigned compOp)92     void keepCompletionOperator(unsigned compOp) { m_completionOperator = compOp; }
keepTypeOfExpression(const QSharedPointer<TypeOfExpression> & typeOfExp)93     void keepTypeOfExpression(const QSharedPointer<TypeOfExpression> &typeOfExp)
94     { m_typeOfExpression = typeOfExp; }
isKeyword() const95     bool isKeyword() const final
96     { return m_isKeyword; }
setIsKeyword(bool isKeyword)97     void setIsKeyword(bool isKeyword)
98     { m_isKeyword = isKeyword; }
99 
100     quint64 hash() const override;
101 
102 private:
103     QSharedPointer<TypeOfExpression> m_typeOfExpression;
104     unsigned m_completionOperator = T_EOF_SYMBOL;
105     mutable QChar m_typedChar;
106     bool m_isOverloaded = false;
107     bool m_isKeyword = false;
108 };
109 
110 } // Internal
111 } // CppTools
112 
Q_DECLARE_METATYPE(CppTools::Internal::CompleteFunctionDeclaration)113 Q_DECLARE_METATYPE(CppTools::Internal::CompleteFunctionDeclaration)
114 
115 bool CppAssistProposalModel::isSortable(const QString &prefix) const
116 {
117     if (m_completionOperator != T_EOF_SYMBOL)
118         return true;
119 
120     return !prefix.isEmpty();
121 }
122 
proposalItem(int index) const123 AssistProposalItemInterface *CppAssistProposalModel::proposalItem(int index) const
124 {
125     AssistProposalItemInterface *item = GenericProposalModel::proposalItem(index);
126     if (!item->isSnippet()) {
127         auto cppItem = static_cast<CppAssistProposalItem *>(item);
128         cppItem->keepCompletionOperator(m_completionOperator);
129         cppItem->keepTypeOfExpression(m_typeOfExpression);
130     }
131     return item;
132 }
133 
prematurelyApplies(const QChar & typedChar) const134 bool CppAssistProposalItem::prematurelyApplies(const QChar &typedChar) const
135 {
136     if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
137         if (typedChar == QLatin1Char('(') || typedChar == QLatin1Char(',')) {
138             m_typedChar = typedChar;
139             return true;
140         }
141     } else if (m_completionOperator == T_STRING_LITERAL
142                || m_completionOperator == T_ANGLE_STRING_LITERAL) {
143         if (typedChar == QLatin1Char('/') && text().endsWith(QLatin1Char('/'))) {
144             m_typedChar = typedChar;
145             return true;
146         }
147     } else if (data().value<Symbol *>()) {
148         if (typedChar == QLatin1Char(':')
149                 || typedChar == QLatin1Char(';')
150                 || typedChar == QLatin1Char('.')
151                 || typedChar == QLatin1Char(',')
152                 || typedChar == QLatin1Char('(')) {
153             m_typedChar = typedChar;
154             return true;
155         }
156     } else if (data().canConvert<CompleteFunctionDeclaration>()) {
157         if (typedChar == QLatin1Char('(')) {
158             m_typedChar = typedChar;
159             return true;
160         }
161     }
162 
163     return false;
164 }
165 
isDereferenced(TextDocumentManipulatorInterface & manipulator,int basePosition)166 static bool isDereferenced(TextDocumentManipulatorInterface &manipulator, int basePosition)
167 {
168     QTextCursor cursor = manipulator.textCursorAt(basePosition);
169     cursor.setPosition(basePosition);
170 
171     BackwardsScanner scanner(cursor, LanguageFeatures());
172     for (int pos = scanner.startToken()-1; pos >= 0; pos--) {
173         switch (scanner[pos].kind()) {
174         case T_COLON_COLON:
175         case T_IDENTIFIER:
176             //Ignore scope specifiers
177             break;
178 
179         case T_AMPER: return true;
180         default:      return false;
181         }
182     }
183     return false;
184 }
185 
hash() const186 quint64 CppAssistProposalItem::hash() const
187 {
188     if (data().canConvert<Symbol *>())
189         return quint64(data().value<Symbol *>()->index());
190     else if (data().canConvert<CompleteFunctionDeclaration>())
191         return quint64(data().value<CompleteFunctionDeclaration>().function->index());
192 
193     return 0;
194 }
195 
applyContextualContent(TextDocumentManipulatorInterface & manipulator,int basePosition) const196 void CppAssistProposalItem::applyContextualContent(TextDocumentManipulatorInterface &manipulator, int basePosition) const
197 {
198     Symbol *symbol = nullptr;
199 
200     if (data().isValid())
201         symbol = data().value<Symbol *>();
202 
203     QString toInsert;
204     QString extraChars;
205     int extraLength = 0;
206     int cursorOffset = 0;
207     bool setAutoCompleteSkipPos = false;
208 
209     bool autoParenthesesEnabled = true;
210 
211     if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
212         toInsert = text();
213         extraChars += QLatin1Char(')');
214 
215         if (m_typedChar == QLatin1Char('(')) // Eat the opening parenthesis
216             m_typedChar = QChar();
217     } else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) {
218         toInsert = text();
219         if (!toInsert.endsWith(QLatin1Char('/'))) {
220             extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
221         } else {
222             if (m_typedChar == QLatin1Char('/')) // Eat the slash
223                 m_typedChar = QChar();
224         }
225     } else {
226         toInsert = text();
227 
228         const CompletionSettings &completionSettings = TextEditorSettings::completionSettings();
229         const bool autoInsertBrackets = completionSettings.m_autoInsertBrackets;
230 
231         if (autoInsertBrackets && symbol && symbol->type()) {
232             if (Function *function = symbol->type()->asFunctionType()) {
233                 // If the member is a function, automatically place the opening parenthesis,
234                 // except when it might take template parameters.
235                 if (!function->hasReturnType()
236                     && (function->unqualifiedName()
237                     && !function->unqualifiedName()->isDestructorNameId())) {
238                     // Don't insert any magic, since the user might have just wanted to select the class
239 
240                     /// ### port me
241 #if 0
242                 } else if (function->templateParameterCount() != 0 && typedChar != QLatin1Char('(')) {
243                     // If there are no arguments, then we need the template specification
244                     if (function->argumentCount() == 0)
245                         extraChars += QLatin1Char('<');
246 #endif
247                 } else if (!isDereferenced(manipulator, basePosition) && !function->isAmbiguous()) {
248                     // When the user typed the opening parenthesis, he'll likely also type the closing one,
249                     // in which case it would be annoying if we put the cursor after the already automatically
250                     // inserted closing parenthesis.
251                     const bool skipClosingParenthesis = m_typedChar != QLatin1Char('(');
252 
253                     if (completionSettings.m_spaceAfterFunctionName)
254                         extraChars += QLatin1Char(' ');
255                     extraChars += QLatin1Char('(');
256                     if (m_typedChar == QLatin1Char('('))
257                         m_typedChar = QChar();
258 
259                     // If the function doesn't return anything, automatically place the semicolon,
260                     // unless we're doing a scope completion (then it might be function definition).
261                     const QChar characterAtCursor = manipulator.characterAt(manipulator.currentPosition());
262                     bool endWithSemicolon = m_typedChar == QLatin1Char(';')
263                             || (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON);
264                     const QChar semicolon = m_typedChar.isNull() ? QLatin1Char(';') : m_typedChar;
265 
266                     if (endWithSemicolon && characterAtCursor == semicolon) {
267                         endWithSemicolon = false;
268                         m_typedChar = QChar();
269                     }
270 
271                     // If the function takes no arguments, automatically place the closing parenthesis
272                     if (!isOverloaded() && !function->hasArguments() && skipClosingParenthesis) {
273                         extraChars += QLatin1Char(')');
274                         if (endWithSemicolon) {
275                             extraChars += semicolon;
276                             m_typedChar = QChar();
277                         }
278                     } else if (autoParenthesesEnabled) {
279                         const QChar lookAhead = manipulator.characterAt(manipulator.currentPosition() + 1);
280                         if (MatchingText::shouldInsertMatchingText(lookAhead)) {
281                             extraChars += QLatin1Char(')');
282                             --cursorOffset;
283                             setAutoCompleteSkipPos = true;
284                             if (endWithSemicolon) {
285                                 extraChars += semicolon;
286                                 --cursorOffset;
287                                 m_typedChar = QChar();
288                             }
289                         }
290                         // TODO: When an opening parenthesis exists, the "semicolon" should really be
291                         // inserted after the matching closing parenthesis.
292                     }
293                 }
294             }
295         }
296 
297         if (autoInsertBrackets && data().canConvert<CompleteFunctionDeclaration>()) {
298             if (m_typedChar == QLatin1Char('('))
299                 m_typedChar = QChar();
300 
301             // everything from the closing parenthesis on are extra chars, to
302             // make sure an auto-inserted ")" gets replaced by ") const" if necessary
303             int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
304             extraChars = toInsert.mid(closingParen);
305             toInsert.truncate(closingParen);
306         }
307     }
308 
309     // Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
310     if (!m_typedChar.isNull()) {
311         extraChars += m_typedChar;
312         if (cursorOffset != 0)
313             --cursorOffset;
314     }
315 
316     // Avoid inserting characters that are already there
317     int currentPosition = manipulator.currentPosition();
318     QTextCursor cursor = manipulator.textCursorAt(basePosition);
319     cursor.movePosition(QTextCursor::EndOfWord);
320     const QString textAfterCursor = manipulator.textAt(currentPosition,
321                                                        cursor.position() - currentPosition);
322     if (toInsert != textAfterCursor
323             && toInsert.indexOf(textAfterCursor, currentPosition - basePosition) >= 0) {
324         currentPosition = cursor.position();
325     }
326 
327     for (int i = 0; i < extraChars.length(); ++i) {
328         const QChar a = extraChars.at(i);
329         const QChar b = manipulator.characterAt(currentPosition + i);
330         if (a == b)
331             ++extraLength;
332         else
333             break;
334     }
335 
336     toInsert += extraChars;
337 
338     // Insert the remainder of the name
339     const int length = currentPosition - basePosition + extraLength;
340     manipulator.replace(basePosition, length, toInsert);
341     manipulator.setCursorPosition(basePosition + toInsert.length());
342     if (cursorOffset)
343         manipulator.setCursorPosition(manipulator.currentPosition() + cursorOffset);
344     if (setAutoCompleteSkipPos)
345         manipulator.setAutoCompleteSkipPosition(manipulator.currentPosition());
346 }
347 
348 // --------------------
349 // CppFunctionHintModel
350 // --------------------
351 class CppFunctionHintModel : public IFunctionHintProposalModel
352 {
353 public:
CppFunctionHintModel(const QList<Function * > & functionSymbols,const QSharedPointer<TypeOfExpression> & typeOfExp)354     CppFunctionHintModel(const QList<Function *> &functionSymbols,
355                          const QSharedPointer<TypeOfExpression> &typeOfExp)
356         : m_functionSymbols(functionSymbols)
357         , m_currentArg(-1)
358         , m_typeOfExpression(typeOfExp)
359     {}
360 
reset()361     void reset() override {}
size() const362     int size() const override { return m_functionSymbols.size(); }
363     QString text(int index) const override;
364     int activeArgument(const QString &prefix) const override;
365 
366 private:
367     QList<Function *> m_functionSymbols;
368     mutable int m_currentArg;
369     QSharedPointer<TypeOfExpression> m_typeOfExpression;
370 };
371 
text(int index) const372 QString CppFunctionHintModel::text(int index) const
373 {
374     Overview overview;
375     overview.showReturnTypes = true;
376     overview.showArgumentNames = true;
377     overview.markedArgument = m_currentArg + 1;
378     Function *f = m_functionSymbols.at(index);
379 
380     const QString prettyMethod = overview.prettyType(f->type(), f->name());
381     const int begin = overview.markedArgumentBegin;
382     const int end = overview.markedArgumentEnd;
383 
384     QString hintText;
385     hintText += prettyMethod.left(begin).toHtmlEscaped();
386     hintText += QLatin1String("<b>");
387     hintText += prettyMethod.mid(begin, end - begin).toHtmlEscaped();
388     hintText += QLatin1String("</b>");
389     hintText += prettyMethod.mid(end).toHtmlEscaped();
390     return hintText;
391 }
392 
activeArgument(const QString & prefix) const393 int CppFunctionHintModel::activeArgument(const QString &prefix) const
394 {
395     int argnr = 0;
396     int parcount = 0;
397     SimpleLexer tokenize;
398     Tokens tokens = tokenize(prefix);
399     for (int i = 0; i < tokens.count(); ++i) {
400         const Token &tk = tokens.at(i);
401         if (tk.is(T_LPAREN))
402             ++parcount;
403         else if (tk.is(T_RPAREN))
404             --parcount;
405         else if (!parcount && tk.is(T_COMMA))
406             ++argnr;
407     }
408 
409     if (parcount < 0)
410         return -1;
411 
412     if (argnr != m_currentArg)
413         m_currentArg = argnr;
414 
415     return argnr;
416 }
417 
418 // ---------------------------
419 // InternalCompletionAssistProvider
420 // ---------------------------
421 
createProcessor() const422 IAssistProcessor *InternalCompletionAssistProvider::createProcessor() const
423 {
424     return new InternalCppCompletionAssistProcessor;
425 }
426 
createAssistInterface(const Utils::FilePath & filePath,const TextEditorWidget * textEditorWidget,const LanguageFeatures & languageFeatures,int position,AssistReason reason) const427 AssistInterface *InternalCompletionAssistProvider::createAssistInterface(
428     const Utils::FilePath &filePath,
429     const TextEditorWidget *textEditorWidget,
430     const LanguageFeatures &languageFeatures,
431     int position,
432     AssistReason reason) const
433 {
434     QTC_ASSERT(textEditorWidget, return nullptr);
435 
436     return new CppCompletionAssistInterface(filePath,
437                                             textEditorWidget,
438                                             BuiltinEditorDocumentParser::get(filePath.toString()),
439                                             languageFeatures,
440                                             position,
441                                             reason,
442                                             CppModelManager::instance()->workingCopy());
443 }
444 
445 // -----------------
446 // CppAssistProposal
447 // -----------------
448 class CppAssistProposal : public GenericProposal
449 {
450 public:
CppAssistProposal(int cursorPos,GenericProposalModelPtr model)451     CppAssistProposal(int cursorPos, GenericProposalModelPtr model)
452         : GenericProposal(cursorPos, model)
453         , m_replaceDotForArrow(model.staticCast<CppAssistProposalModel>()->m_replaceDotForArrow)
454     {}
455 
isCorrective(TextEditorWidget *) const456     bool isCorrective(TextEditorWidget *) const override { return m_replaceDotForArrow; }
457     void makeCorrection(TextEditorWidget *editorWidget) override;
458 
459 private:
460     bool m_replaceDotForArrow;
461 };
462 
makeCorrection(TextEditorWidget * editorWidget)463 void CppAssistProposal::makeCorrection(TextEditorWidget *editorWidget)
464 {
465     const int oldPosition = editorWidget->position();
466     editorWidget->setCursorPosition(basePosition() - 1);
467     editorWidget->replace(1, QLatin1String("->"));
468     editorWidget->setCursorPosition(oldPosition + 1);
469     moveBasePosition(1);
470 }
471 
472 namespace {
473 
474 class ConvertToCompletionItem: protected NameVisitor
475 {
476     // The completion item.
477     AssistProposalItem *_item = nullptr;
478 
479     // The current symbol.
480     Symbol *_symbol = nullptr;
481 
482     // The pretty printer.
483     Overview overview;
484 
485 public:
ConvertToCompletionItem()486     ConvertToCompletionItem()
487     {
488         overview.showReturnTypes = true;
489         overview.showArgumentNames = true;
490     }
491 
operator ()(Symbol * symbol)492     AssistProposalItem *operator()(Symbol *symbol)
493     {
494         //using declaration can be qualified
495         if (!symbol || !symbol->name() || (symbol->name()->isQualifiedNameId()
496                                            && !symbol->asUsingDeclaration()))
497             return nullptr;
498 
499         AssistProposalItem *previousItem = switchCompletionItem(nullptr);
500         Symbol *previousSymbol = switchSymbol(symbol);
501         accept(symbol->unqualifiedName());
502         if (_item)
503             _item->setData(QVariant::fromValue(symbol));
504         (void) switchSymbol(previousSymbol);
505         return switchCompletionItem(previousItem);
506     }
507 
508 protected:
switchSymbol(Symbol * symbol)509     Symbol *switchSymbol(Symbol *symbol)
510     {
511         Symbol *previousSymbol = _symbol;
512         _symbol = symbol;
513         return previousSymbol;
514     }
515 
switchCompletionItem(AssistProposalItem * item)516     AssistProposalItem *switchCompletionItem(AssistProposalItem *item)
517     {
518         AssistProposalItem *previousItem = _item;
519         _item = item;
520         return previousItem;
521     }
522 
newCompletionItem(const Name * name)523     AssistProposalItem *newCompletionItem(const Name *name)
524     {
525         AssistProposalItem *item = new CppAssistProposalItem;
526         item->setText(overview.prettyName(name));
527         return item;
528     }
529 
visit(const Identifier * name)530     void visit(const Identifier *name) override
531     {
532         _item = newCompletionItem(name);
533         if (!_symbol->isScope() || _symbol->isFunction())
534             _item->setDetail(overview.prettyType(_symbol->type(), name));
535     }
536 
visit(const TemplateNameId * name)537     void visit(const TemplateNameId *name) override
538     {
539         _item = newCompletionItem(name);
540         _item->setText(QString::fromUtf8(name->identifier()->chars(), name->identifier()->size()));
541     }
542 
visit(const DestructorNameId * name)543     void visit(const DestructorNameId *name) override
544     { _item = newCompletionItem(name); }
545 
visit(const OperatorNameId * name)546     void visit(const OperatorNameId *name) override
547     {
548         _item = newCompletionItem(name);
549         _item->setDetail(overview.prettyType(_symbol->type(), name));
550     }
551 
visit(const ConversionNameId * name)552     void visit(const ConversionNameId *name) override
553     { _item = newCompletionItem(name); }
554 
visit(const QualifiedNameId * name)555     void visit(const QualifiedNameId *name) override
556     { _item = newCompletionItem(name->name()); }
557 };
558 
asClassOrTemplateClassType(FullySpecifiedType ty)559 Class *asClassOrTemplateClassType(FullySpecifiedType ty)
560 {
561     if (Class *classTy = ty->asClassType())
562         return classTy;
563     if (Template *templ = ty->asTemplateType()) {
564         if (Symbol *decl = templ->declaration())
565             return decl->asClass();
566     }
567     return nullptr;
568 }
569 
enclosingNonTemplateScope(Symbol * symbol)570 Scope *enclosingNonTemplateScope(Symbol *symbol)
571 {
572     if (symbol) {
573         if (Scope *scope = symbol->enclosingScope()) {
574             if (Template *templ = scope->asTemplate())
575                 return templ->enclosingScope();
576             return scope;
577         }
578     }
579     return nullptr;
580 }
581 
asFunctionOrTemplateFunctionType(FullySpecifiedType ty)582 Function *asFunctionOrTemplateFunctionType(FullySpecifiedType ty)
583 {
584     if (Function *funTy = ty->asFunctionType())
585         return funTy;
586     if (Template *templ = ty->asTemplateType()) {
587         if (Symbol *decl = templ->declaration())
588             return decl->asFunction();
589     }
590     return nullptr;
591 }
592 
isQPrivateSignal(const Symbol * symbol)593 bool isQPrivateSignal(const Symbol *symbol)
594 {
595     if (!symbol)
596         return false;
597 
598     static Identifier qPrivateSignalIdentifier("QPrivateSignal", 14);
599 
600     if (FullySpecifiedType type = symbol->type()) {
601         if (NamedType *namedType = type->asNamedType()) {
602             if (const Name *name = namedType->name()) {
603                 if (name->match(&qPrivateSignalIdentifier))
604                     return true;
605             }
606         }
607     }
608     return false;
609 }
610 
createQt4SignalOrSlot(CPlusPlus::Function * function,const Overview & overview)611 QString createQt4SignalOrSlot(CPlusPlus::Function *function, const Overview &overview)
612 {
613     QString signature;
614     signature += Overview().prettyName(function->name());
615     signature += QLatin1Char('(');
616     for (unsigned i = 0, to = function->argumentCount(); i < to; ++i) {
617         Symbol *arg = function->argumentAt(i);
618         if (isQPrivateSignal(arg))
619             continue;
620         if (i != 0)
621             signature += QLatin1Char(',');
622         signature += overview.prettyType(arg->type());
623     }
624     signature += QLatin1Char(')');
625 
626     const QByteArray normalized = QMetaObject::normalizedSignature(signature.toUtf8());
627     return QString::fromUtf8(normalized, normalized.size());
628 }
629 
createQt5SignalOrSlot(CPlusPlus::Function * function,const Overview & overview)630 QString createQt5SignalOrSlot(CPlusPlus::Function *function, const Overview &overview)
631 {
632     QString text;
633     text += overview.prettyName(function->name());
634     return text;
635 }
636 
637 /*!
638     \class BackwardsEater
639     \brief Checks strings and expressions before given position.
640 
641     Similar to BackwardsScanner, but also can handle expressions. Ignores whitespace.
642 */
643 class BackwardsEater
644 {
645 public:
BackwardsEater(const CppCompletionAssistInterface * assistInterface,int position)646     explicit BackwardsEater(const CppCompletionAssistInterface *assistInterface, int position)
647         : m_position(position)
648         , m_assistInterface(assistInterface)
649     {
650     }
651 
isPositionValid() const652     bool isPositionValid() const
653     {
654         return m_position >= 0;
655     }
656 
eatConnectOpenParenthesis()657     bool eatConnectOpenParenthesis()
658     {
659         return eatString(QLatin1String("(")) && eatString(QLatin1String("connect"));
660     }
661 
eatExpressionCommaAmpersand()662     bool eatExpressionCommaAmpersand()
663     {
664         return eatString(QLatin1String("&")) && eatString(QLatin1String(",")) && eatExpression();
665     }
666 
eatConnectOpenParenthesisExpressionCommaAmpersandExpressionComma()667     bool eatConnectOpenParenthesisExpressionCommaAmpersandExpressionComma()
668     {
669         return eatString(QLatin1String(","))
670             && eatExpression()
671             && eatExpressionCommaAmpersand()
672             && eatConnectOpenParenthesis();
673     }
674 
675 private:
eatExpression()676     bool eatExpression()
677     {
678         if (!isPositionValid())
679             return false;
680 
681         maybeEatWhitespace();
682 
683         QTextCursor cursor(m_assistInterface->textDocument());
684         cursor.setPosition(m_position + 1);
685         ExpressionUnderCursor expressionUnderCursor(m_assistInterface->languageFeatures());
686         const QString expression = expressionUnderCursor(cursor);
687         if (expression.isEmpty())
688             return false;
689         m_position = m_position - expression.length();
690         return true;
691     }
692 
eatString(const QString & string)693     bool eatString(const QString &string)
694     {
695         if (!isPositionValid())
696             return false;
697 
698         if (string.isEmpty())
699             return true;
700 
701         maybeEatWhitespace();
702 
703         const int stringLength = string.length();
704         const int stringStart = m_position - (stringLength - 1);
705 
706         if (stringStart < 0)
707             return false;
708 
709         if (m_assistInterface->textAt(stringStart, stringLength) == string) {
710             m_position = stringStart - 1;
711             return true;
712         }
713 
714         return false;
715     }
716 
maybeEatWhitespace()717     void maybeEatWhitespace()
718     {
719         while (isPositionValid() && m_assistInterface->characterAt(m_position).isSpace())
720             --m_position;
721     }
722 
723 private:
724     int m_position;
725     const CppCompletionAssistInterface * const m_assistInterface;
726 };
727 
canCompleteConnectSignalAt2ndArgument(const CppCompletionAssistInterface * assistInterface,int startOfExpression)728 bool canCompleteConnectSignalAt2ndArgument(const CppCompletionAssistInterface *assistInterface,
729                                            int startOfExpression)
730 {
731     BackwardsEater eater(assistInterface, startOfExpression);
732 
733     return eater.isPositionValid()
734         && eater.eatExpressionCommaAmpersand()
735         && eater.eatConnectOpenParenthesis();
736 }
737 
canCompleteConnectSignalAt4thArgument(const CppCompletionAssistInterface * assistInterface,int startPosition)738 bool canCompleteConnectSignalAt4thArgument(const CppCompletionAssistInterface *assistInterface,
739                                            int startPosition)
740 {
741     BackwardsEater eater(assistInterface, startPosition);
742 
743     return eater.isPositionValid()
744         && eater.eatExpressionCommaAmpersand()
745         && eater.eatConnectOpenParenthesisExpressionCommaAmpersandExpressionComma();
746 }
747 
canCompleteClassNameAt2ndOr4thConnectArgument(const CppCompletionAssistInterface * assistInterface,int startPosition)748 bool canCompleteClassNameAt2ndOr4thConnectArgument(
749         const CppCompletionAssistInterface *assistInterface,
750         int startPosition)
751 {
752     BackwardsEater eater(assistInterface, startPosition);
753 
754     if (!eater.isPositionValid())
755         return false;
756 
757     return eater.eatConnectOpenParenthesis()
758         || eater.eatConnectOpenParenthesisExpressionCommaAmpersandExpressionComma();
759 }
760 
classOrNamespaceFromLookupItem(const LookupItem & lookupItem,const LookupContext & context)761 ClassOrNamespace *classOrNamespaceFromLookupItem(const LookupItem &lookupItem,
762                                                  const LookupContext &context)
763 {
764     const Name *name = nullptr;
765 
766     if (Symbol *d = lookupItem.declaration()) {
767         if (Class *k = d->asClass())
768             name = k->name();
769     }
770 
771     if (!name) {
772         FullySpecifiedType type = lookupItem.type().simplified();
773 
774         if (PointerType *pointerType = type->asPointerType())
775             type = pointerType->elementType().simplified();
776         else
777             return nullptr; // not a pointer or a reference to a pointer.
778 
779         NamedType *namedType = type->asNamedType();
780         if (!namedType) // not a class name.
781             return nullptr;
782 
783         name = namedType->name();
784     }
785 
786     return name ? context.lookupType(name, lookupItem.scope()) : nullptr;
787 }
788 
classFromLookupItem(const LookupItem & lookupItem,const LookupContext & context)789 Class *classFromLookupItem(const LookupItem &lookupItem, const LookupContext &context)
790 {
791     ClassOrNamespace *b = classOrNamespaceFromLookupItem(lookupItem, context);
792     if (!b)
793         return nullptr;
794 
795     foreach (Symbol *s, b->symbols()) {
796         if (Class *klass = s->asClass())
797             return klass;
798     }
799     return nullptr;
800 }
801 
minimalName(Symbol * symbol,Scope * targetScope,const LookupContext & context)802 const Name *minimalName(Symbol *symbol, Scope *targetScope, const LookupContext &context)
803 {
804     ClassOrNamespace *target = context.lookupType(targetScope);
805     if (!target)
806         target = context.globalNamespace();
807     return LookupContext::minimalName(symbol, target, context.bindings()->control().data());
808 }
809 
810 } // Anonymous
811 
812 // ------------------------------------
813 // InternalCppCompletionAssistProcessor
814 // ------------------------------------
InternalCppCompletionAssistProcessor()815 InternalCppCompletionAssistProcessor::InternalCppCompletionAssistProcessor()
816     : m_model(new CppAssistProposalModel)
817 {
818 }
819 
820 InternalCppCompletionAssistProcessor::~InternalCppCompletionAssistProcessor() = default;
821 
perform(const AssistInterface * interface)822 IAssistProposal * InternalCppCompletionAssistProcessor::perform(const AssistInterface *interface)
823 {
824     m_interface.reset(static_cast<const CppCompletionAssistInterface *>(interface));
825 
826     if (interface->reason() != ExplicitlyInvoked && !accepts())
827         return nullptr;
828 
829     int index = startCompletionHelper();
830     if (index != -1) {
831         if (m_hintProposal)
832             return m_hintProposal;
833 
834         return createContentProposal();
835     }
836 
837     return nullptr;
838 }
839 
accepts() const840 bool InternalCppCompletionAssistProcessor::accepts() const
841 {
842     const int pos = m_interface->position();
843     unsigned token = T_EOF_SYMBOL;
844 
845     const int start = startOfOperator(pos, &token, /*want function call=*/ true);
846     if (start != pos) {
847         if (token == T_POUND) {
848             const int column = pos - m_interface->textDocument()->findBlock(start).position();
849             if (column != 1)
850                 return false;
851         }
852 
853         return true;
854     } else {
855         // Trigger completion after n characters of a name have been typed, when not editing an existing name
856         QChar characterUnderCursor = m_interface->characterAt(pos);
857 
858         if (!isValidIdentifierChar(characterUnderCursor)) {
859             const int startOfName = findStartOfName(pos);
860             if (pos - startOfName >= TextEditorSettings::completionSettings().m_characterThreshold) {
861                 const QChar firstCharacter = m_interface->characterAt(startOfName);
862                 if (isValidFirstIdentifierChar(firstCharacter)) {
863                     // Finally check that we're not inside a comment or string (code copied from startOfOperator)
864                     QTextCursor tc(m_interface->textDocument());
865                     tc.setPosition(pos);
866 
867                     SimpleLexer tokenize;
868                     tokenize.setLanguageFeatures(m_interface->languageFeatures());
869                     tokenize.setSkipComments(false);
870 
871                     const Tokens &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
872                     const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
873                     const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
874 
875                     if (!tk.isComment() && !tk.isLiteral()) {
876                         return true;
877                     } else if (tk.isLiteral()
878                                && tokens.size() == 3
879                                && tokens.at(0).kind() == T_POUND
880                                && tokens.at(1).kind() == T_IDENTIFIER) {
881                         const QString &line = tc.block().text();
882                         const Token &idToken = tokens.at(1);
883                         QStringView identifier = idToken.utf16charsEnd() > line.size()
884                                                             ? QStringView(line).mid(
885                                                                 idToken.utf16charsBegin())
886                                                             : QStringView(line)
887                                                                   .mid(idToken.utf16charsBegin(),
888                                                                        idToken.utf16chars());
889                         if (identifier == QLatin1String("include")
890                                 || identifier == QLatin1String("include_next")
891                                 || (m_interface->languageFeatures().objCEnabled && identifier == QLatin1String("import"))) {
892                             return true;
893                         }
894                     }
895                 }
896             }
897         }
898     }
899 
900     return false;
901 }
902 
createContentProposal()903 IAssistProposal *InternalCppCompletionAssistProcessor::createContentProposal()
904 {
905     // Duplicates are kept only if they are snippets.
906     QSet<QString> processed;
907     auto it = m_completions.begin();
908     while (it != m_completions.end()) {
909         auto item = static_cast<CppAssistProposalItem *>(*it);
910         if (!processed.contains(item->text()) || item->isSnippet()) {
911             ++it;
912             if (!item->isSnippet()) {
913                 processed.insert(item->text());
914                 if (!item->isOverloaded()) {
915                     if (auto symbol = qvariant_cast<Symbol *>(item->data())) {
916                         if (Function *funTy = symbol->type()->asFunctionType()) {
917                             if (funTy->hasArguments())
918                                 item->markAsOverloaded();
919                         }
920                     }
921                 }
922             }
923         } else {
924             delete *it;
925             it = m_completions.erase(it);
926         }
927     }
928 
929     m_model->loadContent(m_completions);
930     return new CppAssistProposal(m_positionForProposal, m_model);
931 }
932 
createHintProposal(QList<Function * > functionSymbols) const933 IAssistProposal *InternalCppCompletionAssistProcessor::createHintProposal(
934     QList<Function *> functionSymbols) const
935 {
936     FunctionHintProposalModelPtr model(new CppFunctionHintModel(functionSymbols,
937                                                                 m_model->m_typeOfExpression));
938     return new FunctionHintProposal(m_positionForProposal, model);
939 }
940 
startOfOperator(int positionInDocument,unsigned * kind,bool wantFunctionCall) const941 int InternalCppCompletionAssistProcessor::startOfOperator(int positionInDocument,
942                                                           unsigned *kind,
943                                                           bool wantFunctionCall) const
944 {
945     const QChar ch  = m_interface->characterAt(positionInDocument - 1);
946     const QChar ch2 = m_interface->characterAt(positionInDocument - 2);
947     const QChar ch3 = m_interface->characterAt(positionInDocument - 3);
948 
949     int start = positionInDocument
950                  - CppCompletionAssistProvider::activationSequenceChar(ch, ch2, ch3, kind,
951                                                                        wantFunctionCall,
952                                                                        /*wantQt5SignalSlots*/ true);
953 
954     const auto dotAtIncludeCompletionHandler = [this](int &start, unsigned *kind) {
955             start = findStartOfName(start);
956             const QChar ch4 = m_interface->characterAt(start - 1);
957             const QChar ch5 = m_interface->characterAt(start - 2);
958             const QChar ch6 = m_interface->characterAt(start - 3);
959             start = start - CppCompletionAssistProvider::activationSequenceChar(
960                                 ch4, ch5, ch6, kind, false, false);
961     };
962 
963     CppCompletionAssistProcessor::startOfOperator(m_interface->textDocument(),
964                                                   positionInDocument,
965                                                   kind,
966                                                   start,
967                                                   m_interface->languageFeatures(),
968                                                   /*adjustForQt5SignalSlotCompletion=*/ true,
969                                                   dotAtIncludeCompletionHandler);
970     return start;
971 }
972 
findStartOfName(int pos) const973 int InternalCppCompletionAssistProcessor::findStartOfName(int pos) const
974 {
975     if (pos == -1)
976         pos = m_interface->position();
977     QChar chr;
978 
979     // Skip to the start of a name
980     do {
981         chr = m_interface->characterAt(--pos);
982     } while (CppTools::isValidIdentifierChar(chr));
983 
984     return pos + 1;
985 }
986 
startCompletionHelper()987 int InternalCppCompletionAssistProcessor::startCompletionHelper()
988 {
989     if (m_interface->languageFeatures().objCEnabled) {
990         if (tryObjCCompletion())
991             return m_positionForProposal;
992     }
993 
994     const int startOfName = findStartOfName();
995     m_positionForProposal = startOfName;
996     m_model->m_completionOperator = T_EOF_SYMBOL;
997 
998     int endOfOperator = m_positionForProposal;
999 
1000     // Skip whitespace preceding this position
1001     while (m_interface->characterAt(endOfOperator - 1).isSpace())
1002         --endOfOperator;
1003 
1004     int endOfExpression = startOfOperator(endOfOperator,
1005                                           &m_model->m_completionOperator,
1006                                           /*want function call =*/ true);
1007 
1008     if (m_model->m_completionOperator == T_DOXY_COMMENT) {
1009         for (int i = 1; i < T_DOXY_LAST_TAG; ++i)
1010             addCompletionItem(QString::fromLatin1(doxygenTagSpell(i)), Icons::keywordIcon());
1011         return m_positionForProposal;
1012     }
1013 
1014     // Pre-processor completion
1015     if (m_model->m_completionOperator == T_POUND) {
1016         completePreprocessor();
1017         m_positionForProposal = startOfName;
1018         return m_positionForProposal;
1019     }
1020 
1021     // Include completion
1022     if (m_model->m_completionOperator == T_STRING_LITERAL
1023         || m_model->m_completionOperator == T_ANGLE_STRING_LITERAL
1024         || m_model->m_completionOperator == T_SLASH) {
1025 
1026         QTextCursor c(m_interface->textDocument());
1027         c.setPosition(endOfExpression);
1028         if (completeInclude(c))
1029             m_positionForProposal = endOfExpression + 1;
1030         return m_positionForProposal;
1031     }
1032 
1033     ExpressionUnderCursor expressionUnderCursor(m_interface->languageFeatures());
1034     QTextCursor tc(m_interface->textDocument());
1035 
1036     if (m_model->m_completionOperator == T_COMMA) {
1037         tc.setPosition(endOfExpression);
1038         const int start = expressionUnderCursor.startOfFunctionCall(tc);
1039         if (start == -1) {
1040             m_model->m_completionOperator = T_EOF_SYMBOL;
1041             return -1;
1042         }
1043 
1044         endOfExpression = start;
1045         m_positionForProposal = start + 1;
1046         m_model->m_completionOperator = T_LPAREN;
1047     }
1048 
1049     QString expression;
1050     int startOfExpression = m_interface->position();
1051     tc.setPosition(endOfExpression);
1052 
1053     if (m_model->m_completionOperator) {
1054         expression = expressionUnderCursor(tc);
1055         startOfExpression = endOfExpression - expression.length();
1056 
1057         if (m_model->m_completionOperator == T_AMPER) {
1058             // We expect 'expression' to be either "sender" or "receiver" in
1059             //  "connect(sender, &" or
1060             //  "connect(otherSender, &Foo::signal1, receiver, &"
1061             const int beforeExpression = startOfExpression - 1;
1062             if (canCompleteClassNameAt2ndOr4thConnectArgument(m_interface.data(),
1063                                                               beforeExpression)) {
1064                 m_model->m_completionOperator = CompleteQt5SignalOrSlotClassNameTrigger;
1065             } else { // Ensure global completion
1066                 startOfExpression = endOfExpression = m_positionForProposal;
1067                 expression.clear();
1068                 m_model->m_completionOperator = T_EOF_SYMBOL;
1069             }
1070         } else if (m_model->m_completionOperator == T_COLON_COLON) {
1071             // We expect 'expression' to be "Foo" in
1072             //  "connect(sender, &Foo::" or
1073             //  "connect(sender, &Bar::signal1, receiver, &Foo::"
1074             const int beforeExpression = startOfExpression - 1;
1075             if (canCompleteConnectSignalAt2ndArgument(m_interface.data(), beforeExpression))
1076                 m_model->m_completionOperator = CompleteQt5SignalTrigger;
1077             else if (canCompleteConnectSignalAt4thArgument(m_interface.data(), beforeExpression))
1078                 m_model->m_completionOperator = CompleteQt5SlotTrigger;
1079         } else if (m_model->m_completionOperator == T_LPAREN) {
1080             if (expression.endsWith(QLatin1String("SIGNAL"))) {
1081                 m_model->m_completionOperator = T_SIGNAL;
1082             } else if (expression.endsWith(QLatin1String("SLOT"))) {
1083                 m_model->m_completionOperator = T_SLOT;
1084             } else if (m_interface->position() != endOfOperator) {
1085                 // We don't want a function completion when the cursor isn't at the opening brace
1086                 expression.clear();
1087                 m_model->m_completionOperator = T_EOF_SYMBOL;
1088                 m_positionForProposal = startOfName;
1089                 startOfExpression = m_interface->position();
1090             }
1091         }
1092     } else if (expression.isEmpty()) {
1093         while (startOfExpression > 0 && m_interface->characterAt(startOfExpression).isSpace())
1094             --startOfExpression;
1095     }
1096 
1097     int line = 0, column = 0;
1098     Utils::Text::convertPosition(m_interface->textDocument(), startOfExpression, &line, &column);
1099     const QString fileName = m_interface->filePath().toString();
1100     return startCompletionInternal(fileName, line, column - 1, expression, endOfExpression);
1101 }
1102 
tryObjCCompletion()1103 bool InternalCppCompletionAssistProcessor::tryObjCCompletion()
1104 {
1105     int end = m_interface->position();
1106     while (m_interface->characterAt(end).isSpace())
1107         ++end;
1108     if (m_interface->characterAt(end) != QLatin1Char(']'))
1109         return false;
1110 
1111     QTextCursor tc(m_interface->textDocument());
1112     tc.setPosition(end);
1113     BackwardsScanner tokens(tc, m_interface->languageFeatures());
1114     if (tokens[tokens.startToken() - 1].isNot(T_RBRACKET))
1115         return false;
1116 
1117     const int start = tokens.startOfMatchingBrace(tokens.startToken());
1118     if (start == tokens.startToken())
1119         return false;
1120 
1121     const int startPos = tokens[start].bytesBegin() + tokens.startPosition();
1122     const QString expr = m_interface->textAt(startPos, m_interface->position() - startPos);
1123 
1124     Document::Ptr thisDocument = m_interface->snapshot().document(m_interface->filePath());
1125     if (!thisDocument)
1126         return false;
1127 
1128     m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());
1129 
1130     int line = 0, column = 0;
1131     Utils::Text::convertPosition(m_interface->textDocument(), m_interface->position(), &line,
1132                                  &column);
1133     Scope *scope = thisDocument->scopeAt(line, column - 1);
1134     if (!scope)
1135         return false;
1136 
1137     const QList<LookupItem> items = (*m_model->m_typeOfExpression)(expr.toUtf8(), scope);
1138     LookupContext lookupContext(thisDocument, m_interface->snapshot());
1139 
1140     foreach (const LookupItem &item, items) {
1141         FullySpecifiedType ty = item.type().simplified();
1142         if (ty->isPointerType()) {
1143             ty = ty->asPointerType()->elementType().simplified();
1144 
1145             if (NamedType *namedTy = ty->asNamedType()) {
1146                 ClassOrNamespace *binding = lookupContext.lookupType(namedTy->name(), item.scope());
1147                 completeObjCMsgSend(binding, false);
1148             }
1149         } else {
1150             if (ObjCClass *clazz = ty->asObjCClassType()) {
1151                 ClassOrNamespace *binding = lookupContext.lookupType(clazz->name(), item.scope());
1152                 completeObjCMsgSend(binding, true);
1153             }
1154         }
1155     }
1156 
1157     if (m_completions.isEmpty())
1158         return false;
1159 
1160     m_positionForProposal = m_interface->position();
1161     return true;
1162 }
1163 
1164 namespace {
1165 enum CompletionOrder {
1166     // default order is 0
1167     FunctionArgumentsOrder = 2,
1168     FunctionLocalsOrder = 2, // includes local types
1169     PublicClassMemberOrder = 1,
1170     InjectedClassNameOrder = -1,
1171     MacrosOrder = -2,
1172     KeywordsOrder = -2
1173 };
1174 }
1175 
addCompletionItem(const QString & text,const QIcon & icon,int order,const QVariant & data)1176 void InternalCppCompletionAssistProcessor::addCompletionItem(const QString &text,
1177                                                              const QIcon &icon,
1178                                                              int order,
1179                                                              const QVariant &data)
1180 {
1181     AssistProposalItem *item = new CppAssistProposalItem;
1182     item->setText(text);
1183     item->setIcon(icon);
1184     item->setOrder(order);
1185     item->setData(data);
1186     m_completions.append(item);
1187 }
1188 
addCompletionItem(Symbol * symbol,int order)1189 void InternalCppCompletionAssistProcessor::addCompletionItem(Symbol *symbol, int order)
1190 {
1191     ConvertToCompletionItem toCompletionItem;
1192     AssistProposalItem *item = toCompletionItem(symbol);
1193     if (item) {
1194         item->setIcon(Icons::iconForSymbol(symbol));
1195         item->setOrder(order);
1196         m_completions.append(item);
1197     }
1198 }
1199 
completeObjCMsgSend(ClassOrNamespace * binding,bool staticClassAccess)1200 void InternalCppCompletionAssistProcessor::completeObjCMsgSend(ClassOrNamespace *binding,
1201                                                                bool staticClassAccess)
1202 {
1203     QList<Scope*> memberScopes;
1204     foreach (Symbol *s, binding->symbols()) {
1205         if (ObjCClass *c = s->asObjCClass())
1206             memberScopes.append(c);
1207     }
1208 
1209     foreach (Scope *scope, memberScopes) {
1210         for (int i = 0; i < scope->memberCount(); ++i) {
1211             Symbol *symbol = scope->memberAt(i);
1212 
1213             if (ObjCMethod *method = symbol->type()->asObjCMethodType()) {
1214                 if (method->isStatic() == staticClassAccess) {
1215                     Overview oo;
1216                     const SelectorNameId *selectorName =
1217                             method->name()->asSelectorNameId();
1218                     QString text;
1219                     QString data;
1220                     if (selectorName->hasArguments()) {
1221                         for (int i = 0; i < selectorName->nameCount(); ++i) {
1222                             if (i > 0)
1223                                 text += QLatin1Char(' ');
1224                             Symbol *arg = method->argumentAt(i);
1225                             text += QString::fromUtf8(selectorName->nameAt(i)->identifier()->chars());
1226                             text += QLatin1Char(':');
1227                             text += Snippet::kVariableDelimiter;
1228                             text += QLatin1Char('(');
1229                             text += oo.prettyType(arg->type());
1230                             text += QLatin1Char(')');
1231                             text += oo.prettyName(arg->name());
1232                             text += Snippet::kVariableDelimiter;
1233                         }
1234                     } else {
1235                         text = QString::fromUtf8(selectorName->identifier()->chars());
1236                     }
1237                     data = text;
1238 
1239                     if (!text.isEmpty())
1240                         addCompletionItem(text, QIcon(), 0, QVariant::fromValue(data));
1241                 }
1242             }
1243         }
1244     }
1245 }
1246 
completeInclude(const QTextCursor & cursor)1247 bool InternalCppCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
1248 {
1249     QString directoryPrefix;
1250     if (m_model->m_completionOperator == T_SLASH) {
1251         QTextCursor c = cursor;
1252         c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
1253         QString sel = c.selectedText();
1254         int startCharPos = sel.indexOf(QLatin1Char('"'));
1255         if (startCharPos == -1) {
1256             startCharPos = sel.indexOf(QLatin1Char('<'));
1257             m_model->m_completionOperator = T_ANGLE_STRING_LITERAL;
1258         } else {
1259             m_model->m_completionOperator = T_STRING_LITERAL;
1260         }
1261         if (startCharPos != -1)
1262             directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
1263     }
1264 
1265     // Make completion for all relevant includes
1266     ProjectExplorer::HeaderPaths headerPaths = m_interface->headerPaths();
1267     const ProjectExplorer::HeaderPath currentFilePath(m_interface->filePath().toFileInfo().path(),
1268                                                       ProjectExplorer::HeaderPathType::User);
1269     if (!headerPaths.contains(currentFilePath))
1270         headerPaths.append(currentFilePath);
1271 
1272     const QStringList suffixes = Utils::mimeTypeForName(QLatin1String("text/x-c++hdr")).suffixes();
1273 
1274     foreach (const ProjectExplorer::HeaderPath &headerPath, headerPaths) {
1275         QString realPath = headerPath.path;
1276         if (!directoryPrefix.isEmpty()) {
1277             realPath += QLatin1Char('/');
1278             realPath += directoryPrefix;
1279             if (headerPath.type == ProjectExplorer::HeaderPathType::Framework)
1280                 realPath += QLatin1String(".framework/Headers");
1281         }
1282         completeInclude(realPath, suffixes);
1283     }
1284 
1285     return !m_completions.isEmpty();
1286 }
1287 
completeInclude(const QString & realPath,const QStringList & suffixes)1288 void InternalCppCompletionAssistProcessor::completeInclude(const QString &realPath,
1289                                                            const QStringList &suffixes)
1290 {
1291     QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
1292     while (i.hasNext()) {
1293         const QString fileName = i.next();
1294         const QFileInfo fileInfo = i.fileInfo();
1295         const QString suffix = fileInfo.suffix();
1296         if (suffix.isEmpty() || suffixes.contains(suffix)) {
1297             QString text = fileName.mid(realPath.length() + 1);
1298             if (fileInfo.isDir())
1299                 text += QLatin1Char('/');
1300             addCompletionItem(text, Icons::keywordIcon());
1301         }
1302     }
1303 }
1304 
completePreprocessor()1305 void InternalCppCompletionAssistProcessor::completePreprocessor()
1306 {
1307     foreach (const QString &preprocessorCompletion, m_preprocessorCompletions)
1308         addCompletionItem(preprocessorCompletion);
1309 
1310     if (objcKeywordsWanted())
1311         addCompletionItem(QLatin1String("import"));
1312 }
1313 
objcKeywordsWanted() const1314 bool InternalCppCompletionAssistProcessor::objcKeywordsWanted() const
1315 {
1316     if (!m_interface->languageFeatures().objCEnabled)
1317         return false;
1318 
1319     const Utils::MimeType mt = Utils::mimeTypeForFile(m_interface->filePath());
1320     return mt.matchesName(QLatin1String(CppTools::Constants::OBJECTIVE_C_SOURCE_MIMETYPE))
1321             || mt.matchesName(QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE));
1322 }
1323 
startCompletionInternal(const QString & fileName,int line,int positionInBlock,const QString & expr,int endOfExpression)1324 int InternalCppCompletionAssistProcessor::startCompletionInternal(const QString &fileName,
1325                                                                   int line,
1326                                                                   int positionInBlock,
1327                                                                   const QString &expr,
1328                                                                   int endOfExpression)
1329 {
1330     QString expression = expr.trimmed();
1331 
1332     Document::Ptr thisDocument = m_interface->snapshot().document(fileName);
1333     if (!thisDocument)
1334         return -1;
1335 
1336     m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());
1337 
1338     Scope *scope = thisDocument->scopeAt(line, positionInBlock);
1339     QTC_ASSERT(scope, return -1);
1340 
1341     if (expression.isEmpty()) {
1342         if (m_model->m_completionOperator == T_EOF_SYMBOL || m_model->m_completionOperator == T_COLON_COLON) {
1343             (void) (*m_model->m_typeOfExpression)(expression.toUtf8(), scope);
1344             return globalCompletion(scope) ? m_positionForProposal : -1;
1345         }
1346 
1347         if (m_model->m_completionOperator == T_SIGNAL || m_model->m_completionOperator == T_SLOT) {
1348             // Apply signal/slot completion on 'this'
1349             expression = QLatin1String("this");
1350         }
1351     }
1352 
1353     QByteArray utf8Exp = expression.toUtf8();
1354     QList<LookupItem> results =
1355             (*m_model->m_typeOfExpression)(utf8Exp, scope, TypeOfExpression::Preprocess);
1356 
1357     if (results.isEmpty()) {
1358         if (m_model->m_completionOperator == T_SIGNAL || m_model->m_completionOperator == T_SLOT) {
1359             if (!(expression.isEmpty() || expression == QLatin1String("this"))) {
1360                 expression = QLatin1String("this");
1361                 results = (*m_model->m_typeOfExpression)(utf8Exp, scope);
1362             }
1363 
1364             if (results.isEmpty())
1365                 return -1;
1366 
1367         } else if (m_model->m_completionOperator == T_LPAREN) {
1368             // Find the expression that precedes the current name
1369             int index = endOfExpression;
1370             while (m_interface->characterAt(index - 1).isSpace())
1371                 --index;
1372             index = findStartOfName(index);
1373 
1374             QTextCursor tc(m_interface->textDocument());
1375             tc.setPosition(index);
1376 
1377             ExpressionUnderCursor expressionUnderCursor(m_interface->languageFeatures());
1378             const QString baseExpression = expressionUnderCursor(tc);
1379 
1380             // Resolve the type of this expression
1381             const QList<LookupItem> results =
1382                     (*m_model->m_typeOfExpression)(baseExpression.toUtf8(), scope,
1383                                      TypeOfExpression::Preprocess);
1384 
1385             // If it's a class, add completions for the constructors
1386             foreach (const LookupItem &result, results) {
1387                 if (result.type()->isClassType()) {
1388                     if (completeConstructorOrFunction(results, endOfExpression, true))
1389                         return m_positionForProposal;
1390 
1391                     break;
1392                 }
1393             }
1394             return -1;
1395 
1396         } else if (m_model->m_completionOperator == CompleteQt5SignalOrSlotClassNameTrigger) {
1397             // Fallback to global completion if we could not lookup sender/receiver object.
1398             return globalCompletion(scope) ? m_positionForProposal : -1;
1399 
1400         } else {
1401             return -1; // nothing to do.
1402         }
1403     }
1404 
1405     switch (m_model->m_completionOperator) {
1406     case T_LPAREN:
1407         if (completeConstructorOrFunction(results, endOfExpression, false))
1408             return m_positionForProposal;
1409         break;
1410 
1411     case T_DOT:
1412     case T_ARROW:
1413         if (completeMember(results))
1414             return m_positionForProposal;
1415         break;
1416 
1417     case T_COLON_COLON:
1418         if (completeScope(results))
1419             return m_positionForProposal;
1420         break;
1421 
1422     case T_SIGNAL:
1423         if (completeQtMethod(results, CompleteQt4Signals))
1424             return m_positionForProposal;
1425         break;
1426 
1427     case T_SLOT:
1428         if (completeQtMethod(results, CompleteQt4Slots))
1429             return m_positionForProposal;
1430         break;
1431 
1432     case CompleteQt5SignalOrSlotClassNameTrigger:
1433         if (completeQtMethodClassName(results, scope) || globalCompletion(scope))
1434             return m_positionForProposal;
1435         break;
1436 
1437     case CompleteQt5SignalTrigger:
1438         // Fallback to scope completion if "X::" is a namespace and not a class.
1439         if (completeQtMethod(results, CompleteQt5Signals) || completeScope(results))
1440             return m_positionForProposal;
1441         break;
1442 
1443     case CompleteQt5SlotTrigger:
1444         // Fallback to scope completion if "X::" is a namespace and not a class.
1445         if (completeQtMethod(results, CompleteQt5Slots) || completeScope(results))
1446             return m_positionForProposal;
1447         break;
1448 
1449     default:
1450         break;
1451     } // end of switch
1452 
1453     // nothing to do.
1454     return -1;
1455 }
1456 
globalCompletion(Scope * currentScope)1457 bool InternalCppCompletionAssistProcessor::globalCompletion(Scope *currentScope)
1458 {
1459     const LookupContext &context = m_model->m_typeOfExpression->context();
1460 
1461     if (m_model->m_completionOperator == T_COLON_COLON) {
1462         completeNamespace(context.globalNamespace());
1463         return !m_completions.isEmpty();
1464     }
1465 
1466     QList<ClassOrNamespace *> usingBindings;
1467     ClassOrNamespace *currentBinding = nullptr;
1468 
1469     for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) {
1470         if (Block *block = scope->asBlock()) {
1471             if (ClassOrNamespace *binding = context.lookupType(scope)) {
1472                 for (int i = 0; i < scope->memberCount(); ++i) {
1473                     Symbol *member = scope->memberAt(i);
1474                     if (member->isEnum()) {
1475                         if (ClassOrNamespace *b = binding->findBlock(block))
1476                             completeNamespace(b);
1477                     }
1478                     if (!member->name())
1479                         continue;
1480                     if (UsingNamespaceDirective *u = member->asUsingNamespaceDirective()) {
1481                         if (ClassOrNamespace *b = binding->lookupType(u->name()))
1482                             usingBindings.append(b);
1483                     } else if (Class *c = member->asClass()) {
1484                         if (c->name()->isAnonymousNameId()) {
1485                             if (ClassOrNamespace *b = binding->findBlock(block))
1486                                 completeClass(b);
1487                         }
1488                     }
1489                 }
1490             }
1491         } else if (scope->isFunction() || scope->isClass() || scope->isNamespace()) {
1492             currentBinding = context.lookupType(scope);
1493             break;
1494         }
1495     }
1496 
1497     for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) {
1498         if (scope->isBlock()) {
1499             for (int i = 0; i < scope->memberCount(); ++i)
1500                 addCompletionItem(scope->memberAt(i), FunctionLocalsOrder);
1501         } else if (Function *fun = scope->asFunction()) {
1502             for (int i = 0, argc = fun->argumentCount(); i < argc; ++i)
1503                 addCompletionItem(fun->argumentAt(i), FunctionArgumentsOrder);
1504         } else if (Template *templ = scope->asTemplate()) {
1505             for (int i = 0, argc = templ->templateParameterCount(); i < argc; ++i)
1506                 addCompletionItem(templ->templateParameterAt(i), FunctionArgumentsOrder);
1507             break;
1508         }
1509     }
1510 
1511     QSet<ClassOrNamespace *> processed;
1512     for (; currentBinding; currentBinding = currentBinding->parent()) {
1513         if (processed.contains(currentBinding))
1514             break;
1515         processed.insert(currentBinding);
1516 
1517         foreach (ClassOrNamespace* u, currentBinding->usings())
1518             usingBindings.append(u);
1519 
1520         const QList<Symbol *> symbols = currentBinding->symbols();
1521 
1522         if (!symbols.isEmpty()) {
1523             if (symbols.first()->isClass())
1524                 completeClass(currentBinding);
1525             else
1526                 completeNamespace(currentBinding);
1527         }
1528     }
1529 
1530     foreach (ClassOrNamespace *b, usingBindings)
1531         completeNamespace(b);
1532 
1533     addKeywords();
1534     addMacros(CppModelManager::configurationFileName(), context.snapshot());
1535     addMacros(context.thisDocument()->fileName(), context.snapshot());
1536     addSnippets();
1537     return !m_completions.isEmpty();
1538 }
1539 
addKeywordCompletionItem(const QString & text)1540 void InternalCppCompletionAssistProcessor::addKeywordCompletionItem(const QString &text)
1541 {
1542     auto item = new CppAssistProposalItem;
1543     item->setText(text);
1544     item->setIcon(Icons::keywordIcon());
1545     item->setOrder(KeywordsOrder);
1546     item->setIsKeyword(true);
1547     m_completions.append(item);
1548 }
1549 
completeMember(const QList<LookupItem> & baseResults)1550 bool InternalCppCompletionAssistProcessor::completeMember(const QList<LookupItem> &baseResults)
1551 {
1552     const LookupContext &context = m_model->m_typeOfExpression->context();
1553 
1554     if (baseResults.isEmpty())
1555         return false;
1556 
1557     ResolveExpression resolveExpression(context);
1558 
1559     bool *replaceDotForArrow = nullptr;
1560     if (!m_interface->languageFeatures().objCEnabled)
1561         replaceDotForArrow = &m_model->m_replaceDotForArrow;
1562 
1563     if (ClassOrNamespace *binding =
1564             resolveExpression.baseExpression(baseResults,
1565                                              m_model->m_completionOperator,
1566                                              replaceDotForArrow)) {
1567         if (binding)
1568             completeClass(binding, /*static lookup = */ true);
1569 
1570         return !m_completions.isEmpty();
1571     }
1572 
1573     return false;
1574 }
1575 
completeScope(const QList<LookupItem> & results)1576 bool InternalCppCompletionAssistProcessor::completeScope(const QList<LookupItem> &results)
1577 {
1578     const LookupContext &context = m_model->m_typeOfExpression->context();
1579     if (results.isEmpty())
1580         return false;
1581 
1582     foreach (const LookupItem &result, results) {
1583         FullySpecifiedType ty = result.type();
1584         Scope *scope = result.scope();
1585 
1586         if (NamedType *namedTy = ty->asNamedType()) {
1587             if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
1588                 completeClass(b);
1589                 break;
1590             }
1591 
1592         } else if (Class *classTy = ty->asClassType()) {
1593             if (ClassOrNamespace *b = context.lookupType(classTy)) {
1594                 completeClass(b);
1595                 break;
1596             }
1597 
1598             // it can be class defined inside a block
1599             if (classTy->enclosingScope()->isBlock()) {
1600                 if (ClassOrNamespace *b = context.lookupType(classTy->name(), classTy->enclosingScope())) {
1601                     completeClass(b);
1602                     break;
1603                 }
1604             }
1605 
1606         } else if (Namespace *nsTy = ty->asNamespaceType()) {
1607             if (ClassOrNamespace *b = context.lookupType(nsTy)) {
1608                 completeNamespace(b);
1609                 break;
1610             }
1611 
1612         } else if (Template *templ = ty->asTemplateType()) {
1613             if (!result.binding())
1614                 continue;
1615             if (ClassOrNamespace *b = result.binding()->lookupType(templ->name())) {
1616                 completeClass(b);
1617                 break;
1618             }
1619 
1620         } else if (Enum *e = ty->asEnumType()) {
1621             // it can be class defined inside a block
1622             if (e->enclosingScope()->isBlock()) {
1623                 if (ClassOrNamespace *b = context.lookupType(e)) {
1624                     Block *block = e->enclosingScope()->asBlock();
1625                     if (ClassOrNamespace *bb = b->findBlock(block)) {
1626                         completeNamespace(bb);
1627                         break;
1628                     }
1629                 }
1630             }
1631 
1632             if (ClassOrNamespace *b = context.lookupType(e)) {
1633                 completeNamespace(b);
1634                 break;
1635             }
1636 
1637         }
1638     }
1639 
1640     return !m_completions.isEmpty();
1641 }
1642 
completeNamespace(ClassOrNamespace * b)1643 void InternalCppCompletionAssistProcessor::completeNamespace(ClassOrNamespace *b)
1644 {
1645     QSet<ClassOrNamespace *> bindingsVisited;
1646     QList<ClassOrNamespace *> bindingsToVisit;
1647     bindingsToVisit.append(b);
1648 
1649     while (!bindingsToVisit.isEmpty()) {
1650         ClassOrNamespace *binding = bindingsToVisit.takeFirst();
1651         if (!binding || bindingsVisited.contains(binding))
1652             continue;
1653 
1654         bindingsVisited.insert(binding);
1655         bindingsToVisit += binding->usings();
1656 
1657         QList<Scope *> scopesToVisit;
1658         QSet<Scope *> scopesVisited;
1659 
1660         foreach (Symbol *bb, binding->symbols()) {
1661             if (Scope *scope = bb->asScope())
1662                 scopesToVisit.append(scope);
1663         }
1664 
1665         foreach (Enum *e, binding->unscopedEnums())
1666             scopesToVisit.append(e);
1667 
1668         while (!scopesToVisit.isEmpty()) {
1669             Scope *scope = scopesToVisit.takeFirst();
1670             if (!scope || scopesVisited.contains(scope))
1671                 continue;
1672 
1673             scopesVisited.insert(scope);
1674 
1675             for (Scope::iterator it = scope->memberBegin(); it != scope->memberEnd(); ++it) {
1676                 Symbol *member = *it;
1677                 addCompletionItem(member);
1678             }
1679         }
1680     }
1681 }
1682 
completeClass(ClassOrNamespace * b,bool staticLookup)1683 void InternalCppCompletionAssistProcessor::completeClass(ClassOrNamespace *b, bool staticLookup)
1684 {
1685     QSet<ClassOrNamespace *> bindingsVisited;
1686     QList<ClassOrNamespace *> bindingsToVisit;
1687     bindingsToVisit.append(b);
1688 
1689     while (!bindingsToVisit.isEmpty()) {
1690         ClassOrNamespace *binding = bindingsToVisit.takeFirst();
1691         if (!binding || bindingsVisited.contains(binding))
1692             continue;
1693 
1694         bindingsVisited.insert(binding);
1695         bindingsToVisit += binding->usings();
1696 
1697         QList<Scope *> scopesToVisit;
1698         QSet<Scope *> scopesVisited;
1699 
1700         foreach (Symbol *bb, binding->symbols()) {
1701             if (Class *k = bb->asClass())
1702                 scopesToVisit.append(k);
1703             else if (Block *b = bb->asBlock())
1704                 scopesToVisit.append(b);
1705         }
1706 
1707         foreach (Enum *e, binding->unscopedEnums())
1708             scopesToVisit.append(e);
1709 
1710         while (!scopesToVisit.isEmpty()) {
1711             Scope *scope = scopesToVisit.takeFirst();
1712             if (!scope || scopesVisited.contains(scope))
1713                 continue;
1714 
1715             scopesVisited.insert(scope);
1716 
1717             if (staticLookup)
1718                 addCompletionItem(scope, InjectedClassNameOrder); // add a completion item for the injected class name.
1719 
1720             addClassMembersToCompletion(scope, staticLookup);
1721         }
1722     }
1723 }
1724 
addClassMembersToCompletion(Scope * scope,bool staticLookup)1725 void InternalCppCompletionAssistProcessor::addClassMembersToCompletion(Scope *scope,
1726                                                                        bool staticLookup)
1727 {
1728     if (!scope)
1729         return;
1730 
1731     std::set<Class *> nestedAnonymouses;
1732 
1733     for (Scope::iterator it = scope->memberBegin(); it != scope->memberEnd(); ++it) {
1734         Symbol *member = *it;
1735         if (member->isFriend()
1736                 || member->isQtPropertyDeclaration()
1737                 || member->isQtEnum()) {
1738             continue;
1739         } else if (!staticLookup && (member->isTypedef() ||
1740                                     member->isEnum()    ||
1741                                     member->isClass())) {
1742             continue;
1743         } else if (member->isClass() && member->name()->isAnonymousNameId()) {
1744             nestedAnonymouses.insert(member->asClass());
1745         } else if (member->isDeclaration()) {
1746             Class *declTypeAsClass = member->asDeclaration()->type()->asClassType();
1747             if (declTypeAsClass && declTypeAsClass->name()->isAnonymousNameId())
1748                 nestedAnonymouses.erase(declTypeAsClass);
1749         }
1750 
1751         if (member->isPublic())
1752             addCompletionItem(member, PublicClassMemberOrder);
1753         else
1754             addCompletionItem(member);
1755     }
1756     for (Class *klass : nestedAnonymouses)
1757         addClassMembersToCompletion(klass, staticLookup);
1758 }
1759 
completeQtMethod(const QList<LookupItem> & results,CompleteQtMethodMode type)1760 bool InternalCppCompletionAssistProcessor::completeQtMethod(const QList<LookupItem> &results,
1761                                                             CompleteQtMethodMode type)
1762 {
1763     if (results.isEmpty())
1764         return false;
1765 
1766     const LookupContext &context = m_model->m_typeOfExpression->context();
1767 
1768     ConvertToCompletionItem toCompletionItem;
1769     Overview o;
1770     o.showReturnTypes = false;
1771     o.showArgumentNames = false;
1772     o.showFunctionSignatures = true;
1773 
1774     QSet<QString> signatures;
1775     foreach (const LookupItem &lookupItem, results) {
1776         ClassOrNamespace *b = classOrNamespaceFromLookupItem(lookupItem, context);
1777         if (!b)
1778             continue;
1779 
1780         QList<ClassOrNamespace *>todo;
1781         QSet<ClassOrNamespace *> processed;
1782         QList<Scope *> scopes;
1783         todo.append(b);
1784         while (!todo.isEmpty()) {
1785             ClassOrNamespace *binding = todo.takeLast();
1786             if (!processed.contains(binding)) {
1787                 processed.insert(binding);
1788 
1789                 foreach (Symbol *s, binding->symbols())
1790                     if (Class *clazz = s->asClass())
1791                         scopes.append(clazz);
1792 
1793                 todo.append(binding->usings());
1794             }
1795         }
1796 
1797         const bool wantSignals = type == CompleteQt4Signals || type == CompleteQt5Signals;
1798         const bool wantQt5SignalOrSlot = type == CompleteQt5Signals || type == CompleteQt5Slots;
1799         foreach (Scope *scope, scopes) {
1800             Class *klass = scope->asClass();
1801             if (!klass)
1802                 continue;
1803 
1804             for (int i = 0; i < scope->memberCount(); ++i) {
1805                 Symbol *member = scope->memberAt(i);
1806                 Function *fun = member->type()->asFunctionType();
1807                 if (!fun || fun->isGenerated())
1808                     continue;
1809                 if (wantSignals && !fun->isSignal())
1810                     continue;
1811                 else if (!wantSignals && type == CompleteQt4Slots && !fun->isSlot())
1812                     continue;
1813 
1814                 int count = fun->argumentCount();
1815                 while (true) {
1816                     const QString completionText = wantQt5SignalOrSlot
1817                             ? createQt5SignalOrSlot(fun, o)
1818                             : createQt4SignalOrSlot(fun, o);
1819 
1820                     if (!signatures.contains(completionText)) {
1821                         AssistProposalItem *ci = toCompletionItem(fun);
1822                         if (!ci)
1823                             break;
1824                         signatures.insert(completionText);
1825                         ci->setText(completionText); // fix the completion item.
1826                         ci->setIcon(Icons::iconForSymbol(fun));
1827                         if (wantQt5SignalOrSlot && fun->isSlot())
1828                             ci->setOrder(1);
1829                         m_completions.append(ci);
1830                     }
1831 
1832                     if (count && fun->argumentAt(count - 1)->asArgument()->hasInitializer())
1833                         --count;
1834                     else
1835                         break;
1836                 }
1837             }
1838         }
1839     }
1840 
1841     return !m_completions.isEmpty();
1842 }
1843 
completeQtMethodClassName(const QList<LookupItem> & results,Scope * cursorScope)1844 bool InternalCppCompletionAssistProcessor::completeQtMethodClassName(
1845         const QList<LookupItem> &results, Scope *cursorScope)
1846 {
1847     QTC_ASSERT(cursorScope, return false);
1848 
1849     if (results.isEmpty())
1850         return false;
1851 
1852     const LookupContext &context = m_model->m_typeOfExpression->context();
1853     const QIcon classIcon = Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Class);
1854     Overview overview;
1855 
1856     foreach (const LookupItem &lookupItem, results) {
1857         Class *klass = classFromLookupItem(lookupItem, context);
1858         if (!klass)
1859             continue;
1860         const Name *name = minimalName(klass, cursorScope, context);
1861         QTC_ASSERT(name, continue);
1862 
1863         addCompletionItem(overview.prettyName(name), classIcon);
1864         break;
1865     }
1866 
1867     return !m_completions.isEmpty();
1868 }
1869 
addKeywords()1870 void InternalCppCompletionAssistProcessor::addKeywords()
1871 {
1872     int keywordLimit = T_FIRST_OBJC_AT_KEYWORD;
1873     if (objcKeywordsWanted())
1874         keywordLimit = T_LAST_OBJC_AT_KEYWORD + 1;
1875 
1876     // keyword completion items.
1877     for (int i = T_FIRST_KEYWORD; i < keywordLimit; ++i)
1878         addKeywordCompletionItem(QLatin1String(Token::name(i)));
1879 
1880     // primitive type completion items.
1881     for (int i = T_FIRST_PRIMITIVE; i <= T_LAST_PRIMITIVE; ++i)
1882         addKeywordCompletionItem(QLatin1String(Token::name(i)));
1883 
1884     // "Identifiers with special meaning"
1885     if (m_interface->languageFeatures().cxx11Enabled) {
1886         addKeywordCompletionItem(QLatin1String("override"));
1887         addKeywordCompletionItem(QLatin1String("final"));
1888     }
1889 }
1890 
addMacros(const QString & fileName,const Snapshot & snapshot)1891 void InternalCppCompletionAssistProcessor::addMacros(const QString &fileName,
1892                                                      const Snapshot &snapshot)
1893 {
1894     QSet<QString> processed;
1895     QSet<QString> definedMacros;
1896 
1897     addMacros_helper(snapshot, fileName, &processed, &definedMacros);
1898 
1899     foreach (const QString &macroName, definedMacros)
1900         addCompletionItem(macroName, Icons::macroIcon(), MacrosOrder);
1901 }
1902 
addMacros_helper(const Snapshot & snapshot,const QString & fileName,QSet<QString> * processed,QSet<QString> * definedMacros)1903 void InternalCppCompletionAssistProcessor::addMacros_helper(const Snapshot &snapshot,
1904                                                     const QString &fileName,
1905                                                     QSet<QString> *processed,
1906                                                     QSet<QString> *definedMacros)
1907 {
1908     Document::Ptr doc = snapshot.document(fileName);
1909 
1910     if (!doc || processed->contains(doc->fileName()))
1911         return;
1912 
1913     processed->insert(doc->fileName());
1914 
1915     foreach (const Document::Include &i, doc->resolvedIncludes())
1916         addMacros_helper(snapshot, i.resolvedFileName(), processed, definedMacros);
1917 
1918     foreach (const CPlusPlus::Macro &macro, doc->definedMacros()) {
1919         const QString macroName = macro.nameToQString();
1920         if (!macro.isHidden())
1921             definedMacros->insert(macroName);
1922         else
1923             definedMacros->remove(macroName);
1924     }
1925 }
1926 
completeConstructorOrFunction(const QList<LookupItem> & results,int endOfExpression,bool toolTipOnly)1927 bool InternalCppCompletionAssistProcessor::completeConstructorOrFunction(const QList<LookupItem> &results,
1928                                                                          int endOfExpression,
1929                                                                          bool toolTipOnly)
1930 {
1931     const LookupContext &context = m_model->m_typeOfExpression->context();
1932     QList<Function *> functions;
1933 
1934     foreach (const LookupItem &result, results) {
1935         FullySpecifiedType exprTy = result.type().simplified();
1936 
1937         if (Class *klass = asClassOrTemplateClassType(exprTy)) {
1938             const Name *className = klass->name();
1939             if (!className)
1940                 continue; // nothing to do for anonymous classes.
1941 
1942             for (int i = 0; i < klass->memberCount(); ++i) {
1943                 Symbol *member = klass->memberAt(i);
1944                 const Name *memberName = member->name();
1945 
1946                 if (!memberName)
1947                     continue; // skip anonymous member.
1948 
1949                 else if (memberName->isQualifiedNameId())
1950                     continue; // skip
1951 
1952                 if (Function *funTy = member->type()->asFunctionType()) {
1953                     if (memberName->match(className)) {
1954                         // it's a ctor.
1955                         functions.append(funTy);
1956                     }
1957                 }
1958             }
1959 
1960             break;
1961         }
1962     }
1963 
1964     if (functions.isEmpty()) {
1965         foreach (const LookupItem &result, results) {
1966             FullySpecifiedType ty = result.type().simplified();
1967 
1968             if (Function *fun = asFunctionOrTemplateFunctionType(ty)) {
1969 
1970                 if (!fun->name()) {
1971                     continue;
1972                 } else if (!functions.isEmpty()
1973                            && enclosingNonTemplateScope(functions.first())
1974                                 != enclosingNonTemplateScope(fun)) {
1975                     continue; // skip fun, it's an hidden declaration.
1976                 }
1977 
1978                 bool newOverload = true;
1979 
1980                 foreach (Function *f, functions) {
1981                     if (fun->match(f)) {
1982                         newOverload = false;
1983                         break;
1984                     }
1985                 }
1986 
1987                 if (newOverload)
1988                     functions.append(fun);
1989             }
1990         }
1991     }
1992 
1993     if (functions.isEmpty()) {
1994         const Name *functionCallOp = context.bindings()->control()->operatorNameId(OperatorNameId::FunctionCallOp);
1995 
1996         foreach (const LookupItem &result, results) {
1997             FullySpecifiedType ty = result.type().simplified();
1998             Scope *scope = result.scope();
1999 
2000             if (NamedType *namedTy = ty->asNamedType()) {
2001                 if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
2002                     foreach (const LookupItem &r, b->lookup(functionCallOp)) {
2003                         Symbol *overload = r.declaration();
2004                         FullySpecifiedType overloadTy = overload->type().simplified();
2005 
2006                         if (Function *funTy = overloadTy->asFunctionType())
2007                             functions.append(funTy);
2008                     }
2009                 }
2010             }
2011         }
2012     }
2013 
2014     // There are two different kinds of completion we want to provide:
2015     // 1. If this is a function call, we want to pop up a tooltip that shows the user
2016     // the possible overloads with their argument types and names.
2017     // 2. If this is a function definition, we want to offer autocompletion of
2018     // the function signature.
2019 
2020     // check if function signature autocompletion is appropriate
2021     // Also check if the function name is a destructor name.
2022     bool isDestructor = false;
2023     if (!functions.isEmpty() && !toolTipOnly) {
2024 
2025         // function definitions will only happen in class or namespace scope,
2026         // so get the current location's enclosing scope.
2027 
2028         // get current line and column
2029         int lineSigned = 0, columnSigned = 0;
2030         Utils::Text::convertPosition(m_interface->textDocument(), m_interface->position(),
2031                                      &lineSigned, &columnSigned);
2032         unsigned line = lineSigned, column = columnSigned - 1;
2033 
2034         // find a scope that encloses the current location, starting from the lastVisibileSymbol
2035         // and moving outwards
2036 
2037         Scope *sc = context.thisDocument()->scopeAt(line, column);
2038 
2039         if (sc && (sc->isClass() || sc->isNamespace())) {
2040             // It may still be a function call. If the whole line parses as a function
2041             // declaration, we should be certain that it isn't.
2042             bool autocompleteSignature = false;
2043 
2044             QTextCursor tc(m_interface->textDocument());
2045             tc.setPosition(endOfExpression);
2046             BackwardsScanner bs(tc, m_interface->languageFeatures());
2047             const int startToken = bs.startToken();
2048             int lineStartToken = bs.startOfLine(startToken);
2049             // make sure the required tokens are actually available
2050             bs.LA(startToken - lineStartToken);
2051             QString possibleDecl = bs.mid(lineStartToken).trimmed().append(QLatin1String("();"));
2052 
2053             Document::Ptr doc = Document::create(QLatin1String("<completion>"));
2054             doc->setUtf8Source(possibleDecl.toUtf8());
2055             if (doc->parse(Document::ParseDeclaration)) {
2056                 doc->check();
2057                 if (SimpleDeclarationAST *sd = doc->translationUnit()->ast()->asSimpleDeclaration()) {
2058                     if (sd->declarator_list && sd->declarator_list->value->postfix_declarator_list
2059                         && sd->declarator_list->value->postfix_declarator_list->value->asFunctionDeclarator()) {
2060 
2061                         autocompleteSignature = true;
2062 
2063                         CoreDeclaratorAST *coreDecl = sd->declarator_list->value->core_declarator;
2064                         if (coreDecl && coreDecl->asDeclaratorId() && coreDecl->asDeclaratorId()->name) {
2065                             NameAST *declName = coreDecl->asDeclaratorId()->name;
2066                             if (declName->asDestructorName()) {
2067                                 isDestructor = true;
2068                             } else if (QualifiedNameAST *qName = declName->asQualifiedName()) {
2069                                 if (qName->unqualified_name && qName->unqualified_name->asDestructorName())
2070                                     isDestructor = true;
2071                             }
2072                         }
2073                     }
2074                 }
2075             }
2076 
2077             if (autocompleteSignature && !isDestructor) {
2078                 // set up for rewriting function types with minimally qualified names
2079                 // to do it correctly we'd need the declaration's context and scope, but
2080                 // that'd be too expensive to get here. instead, we just minimize locally
2081                 SubstitutionEnvironment env;
2082                 env.setContext(context);
2083                 env.switchScope(sc);
2084                 ClassOrNamespace *targetCoN = context.lookupType(sc);
2085                 if (!targetCoN)
2086                     targetCoN = context.globalNamespace();
2087                 UseMinimalNames q(targetCoN);
2088                 env.enter(&q);
2089                 Control *control = context.bindings()->control().data();
2090 
2091                 // set up signature autocompletion
2092                 foreach (Function *f, functions) {
2093                     Overview overview;
2094                     overview.showArgumentNames = true;
2095                     overview.showDefaultArguments = false;
2096 
2097                     const FullySpecifiedType localTy = rewriteType(f->type(), &env, control);
2098 
2099                     // gets: "parameter list) cv-spec",
2100                     const QString completion = overview.prettyType(localTy).mid(1);
2101                     if (completion == QLatin1String(")"))
2102                         continue;
2103 
2104                     addCompletionItem(completion, QIcon(), 0,
2105                                       QVariant::fromValue(CompleteFunctionDeclaration(f)));
2106                 }
2107                 return true;
2108             }
2109         }
2110     }
2111 
2112     if (!functions.empty() && !isDestructor) {
2113         m_hintProposal = createHintProposal(functions);
2114         return true;
2115     }
2116 
2117     return false;
2118 }
2119 
getCppSpecifics() const2120 void CppCompletionAssistInterface::getCppSpecifics() const
2121 {
2122     if (m_gotCppSpecifics)
2123         return;
2124     m_gotCppSpecifics = true;
2125 
2126     if (m_parser) {
2127         m_parser->update({CppTools::CppModelManager::instance()->workingCopy(),
2128                           nullptr,
2129                           Language::Cxx,
2130                           false});
2131         m_snapshot = m_parser->snapshot();
2132         m_headerPaths = m_parser->headerPaths();
2133     }
2134 }
2135