diff --git a/src/libs/3rdparty/cplusplus/Token.h b/src/libs/3rdparty/cplusplus/Token.h index 1f18c652c91..25befabbab6 100644 --- a/src/libs/3rdparty/cplusplus/Token.h +++ b/src/libs/3rdparty/cplusplus/Token.h @@ -283,7 +283,8 @@ enum Kind { T___VOLATILE = T_VOLATILE, T___VOLATILE__ = T_VOLATILE, - T___ATTRIBUTE = T___ATTRIBUTE__ + T___ATTRIBUTE = T___ATTRIBUTE__, + T_LAST_TOKEN }; class CPLUSPLUS_EXPORT Token diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index 5c84ec93079..625132f99be 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -2168,6 +2168,94 @@ void CppToolsPlugin::test_completion_data() << QLatin1String("Foo") << QLatin1String("bar")); + const QByteArray commonSignalSlotCompletionTestCode = + "#define SIGNAL(a) #a\n" + "#define SLOT(a) #a\n" + "#define signals public\n" + "#define slots\n" + "#define Q_OBJECT virtual const QMetaObject *metaObject() const;" + "\n" + "class Base : public QObject\n" + "{\n" + " Q_OBJECT\n" + "public:\n" + " void hiddenFunction();\n" + " void baseFunction();\n" + "signals:\n" + " void hiddenSignal();\n" + " void baseSignal1();\n" + " void baseSignal2(int newValue);\n" + "public slots:\n" + " void baseSlot1();\n" + " void baseSlot2(int newValue);\n" + "};\n" + "\n" + "class Derived : public Base\n" + "{\n" + " Q_OBJECT\n" + "public:\n" + " void hiddenFunction();\n" + " void derivedFunction();\n" + "signals:\n" + " void hiddenSignal();\n" + " void derivedSignal1();\n" + " void derivedSignal2(int newValue);\n" + "public slots:\n" + " void derivedSlot1();\n" + " void derivedSlot2(int newValue);\n" + "};\n" + "\n" + "void client()\n" + "{\n" + " Derived *myObject = new Derived;\n" + " @\n" + "}\n"; + + QTest::newRow("SIGNAL(") + << commonSignalSlotCompletionTestCode + << _("connect(myObject, SIGNAL(") << (QStringList() + << QLatin1String("baseSignal1()") + << QLatin1String("baseSignal2(int)") + << QLatin1String("hiddenSignal()") + << QLatin1String("derivedSignal1()") + << QLatin1String("derivedSignal2(int)")); + + QTest::newRow("SLOT(") + << commonSignalSlotCompletionTestCode + << _("connect(myObject, SIGNAL(baseSignal1()), myObject, SLOT(") << (QStringList() + << QLatin1String("baseSlot1()") + << QLatin1String("baseSlot2(int)") + << QLatin1String("derivedSlot1()") + << QLatin1String("derivedSlot2(int)")); + + QTest::newRow("Qt5 signal") + << commonSignalSlotCompletionTestCode + << _("connect(myObject, &") << (QStringList() + << QLatin1String("Base::baseSignal1") + << QLatin1String("Base::baseSignal2") + << QLatin1String("Base::hiddenSignal") + << QLatin1String("Derived::derivedSignal1") + << QLatin1String("Derived::derivedSignal2") + << QLatin1String("Derived::hiddenSignal")); // OK, hidden signal + + QTest::newRow("Qt5 slot") + << commonSignalSlotCompletionTestCode + << _("connect(myObject, &MyObject::timeout, myObject, &") << (QStringList() + << QLatin1String("Base::baseSignal1") + << QLatin1String("Base::baseSignal2") + << QLatin1String("Base::baseSlot1") + << QLatin1String("Base::baseSlot2") + << QLatin1String("Base::baseFunction") + << QLatin1String("Base::hiddenFunction") + << QLatin1String("Base::hiddenSignal") + << QLatin1String("Derived::derivedFunction") + << QLatin1String("Derived::derivedSignal1") + << QLatin1String("Derived::derivedSignal2") + << QLatin1String("Derived::derivedSlot1") + << QLatin1String("Derived::derivedSlot2") + << QLatin1String("Derived::hiddenFunction") + << QLatin1String("Derived::hiddenSignal")); + QTest::newRow("signals_hide_QPrivateSignal") << _( "#define SIGNAL(a) #a\n" "#define SLOT(a) #a\n" diff --git a/src/plugins/cpptools/cppcompletionassist.cpp b/src/plugins/cpptools/cppcompletionassist.cpp index 8057bd1a98b..1283e620a8c 100644 --- a/src/plugins/cpptools/cppcompletionassist.cpp +++ b/src/plugins/cpptools/cppcompletionassist.cpp @@ -596,6 +596,34 @@ bool isQPrivateSignal(const Symbol *symbol) return false; } +QString createQt4SignalOrSlot(CPlusPlus::Function *function, const Overview &overview) +{ + QString signature; + signature += Overview().prettyName(function->name()); + signature += QLatin1Char('('); + for (unsigned i = 0, to = function->argumentCount(); i < to; ++i) { + Symbol *arg = function->argumentAt(i); + if (isQPrivateSignal(arg)) + continue; + if (i != 0) + signature += QLatin1Char(','); + signature += overview.prettyType(arg->type()); + } + signature += QLatin1Char(')'); + + const QByteArray normalized = QMetaObject::normalizedSignature(signature.toUtf8()); + return QString::fromUtf8(normalized, normalized.size()); +} + +QString createQt5SignalOrSlot(CPlusPlus::Function *function, Class *klass) +{ + QString text; + text += Overview().prettyName(klass->name()); + text += QLatin1String("::"); + text += Overview().prettyName(function->name()); + return text; +} + } // Anonymous // ------------------------------------ @@ -745,7 +773,8 @@ int InternalCppCompletionAssistProcessor::startOfOperator(int pos, const QChar ch2 = pos > 0 ? m_interface->characterAt(pos - 2) : QChar(); const QChar ch3 = pos > 1 ? m_interface->characterAt(pos - 3) : QChar(); - int start = pos - CppCompletionAssistProvider::activationSequenceChar(ch, ch2, ch3, kind, wantFunctionCall); + int start = pos - CppCompletionAssistProvider::activationSequenceChar(ch, ch2, ch3, kind, + wantFunctionCall, /*wantQt5SignalSlots*/ true); if (start != pos) { QTextCursor tc(m_interface->textDocument()); tc.setPosition(pos); @@ -776,7 +805,13 @@ int InternalCppCompletionAssistProcessor::startOfOperator(int pos, const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1)); // get the token at the left of the cursor const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx); - if (*kind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) { + if (*kind == T_AMPER && tokenIdx > 0) { + const Token &previousToken = tokens.at(tokenIdx - 1); + if (previousToken.kind() == T_COMMA) { + start = pos - (tk.utf16charOffset - previousToken.utf16charOffset) - 1; + QTC_CHECK(m_interface->characterAt(start) == QLatin1Char(',')); + } + } else if (*kind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) { *kind = T_EOF_SYMBOL; start = pos; } @@ -836,7 +871,8 @@ int InternalCppCompletionAssistProcessor::startOfOperator(int pos, const QChar ch4 = start > -1 ? m_interface->characterAt(start - 1) : QChar(); const QChar ch5 = start > 0 ? m_interface->characterAt(start - 2) : QChar(); const QChar ch6 = start > 1 ? m_interface->characterAt(start - 3) : QChar(); - start = start - CppCompletionAssistProvider::activationSequenceChar(ch4, ch5, ch6, kind, wantFunctionCall); + start = start - CppCompletionAssistProvider::activationSequenceChar( + ch4, ch5, ch6, kind, wantFunctionCall, false); } } } @@ -859,6 +895,18 @@ int InternalCppCompletionAssistProcessor::findStartOfName(int pos) const return pos + 1; } +static bool isPrecededByConnectAndOpenParenthesis( + const CppCompletionAssistInterface *assistInterface, + int startOfExpression) +{ + QTC_ASSERT(startOfExpression >= 0, return false); + + int beforeExpression = startOfExpression; + while (beforeExpression > 0 && assistInterface->characterAt(--beforeExpression).isSpace()) ; + const int pos = beforeExpression - 7; + return pos >= 0 && assistInterface->textAt(pos, 7) == QLatin1String("connect"); +} + int InternalCppCompletionAssistProcessor::startCompletionHelper() { if (m_languageFeatures.objCEnabled) { @@ -929,7 +977,12 @@ int InternalCppCompletionAssistProcessor::startCompletionHelper() expression = expressionUnderCursor(tc); startOfExpression = endOfExpression - expression.length(); - if (m_model->m_completionOperator == T_LPAREN) { + if (m_model->m_completionOperator == T_AMPER) { + m_model->m_completionOperator + = isPrecededByConnectAndOpenParenthesis(m_interface.data(), startOfExpression) + ? CompleteQt5SignalTrigger + : CompleteQtSlotTrigger; + } else if (m_model->m_completionOperator == T_LPAREN) { if (expression.endsWith(QLatin1String("SIGNAL"))) { m_model->m_completionOperator = T_SIGNAL; } else if (expression.endsWith(QLatin1String("SLOT"))) { @@ -1276,12 +1329,22 @@ int InternalCppCompletionAssistProcessor::startCompletionInternal(const QString break; case T_SIGNAL: - if (completeSignal(results)) + if (completeQtMethod(results, CompleteQt4Signals)) return m_startPosition; break; case T_SLOT: - if (completeSlot(results)) + if (completeQtMethod(results, CompleteQt4Slots)) + return m_startPosition; + break; + + case CompleteQt5SignalTrigger: + if (completeQtMethod(results, CompleteQt5Signals)) + return m_startPosition; + break; + + case CompleteQtSlotTrigger: + if (completeQtMethod(results, CompleteQt5Slots)) return m_startPosition; break; @@ -1581,9 +1644,8 @@ void InternalCppCompletionAssistProcessor::addClassMembersToCompletion(Scope *sc addClassMembersToCompletion(*cit, staticLookup); } -bool InternalCppCompletionAssistProcessor::completeQtMethod( - const QList &results, - bool wantSignals) +bool InternalCppCompletionAssistProcessor::completeQtMethod(const QList &results, + CompleteQtMethodMode type) { if (results.isEmpty()) return false; @@ -1630,46 +1692,35 @@ bool InternalCppCompletionAssistProcessor::completeQtMethod( } } + const bool wantSignals = type == CompleteQt4Signals || type == CompleteQt5Signals; + const bool wantQt5SignalOrSlot = type == CompleteQt5Signals || type == CompleteQt5Slots; foreach (Scope *scope, scopes) { - if (!scope->isClass()) + Class *klass = scope->asClass(); + if (!klass) continue; for (unsigned i = 0; i < scope->memberCount(); ++i) { Symbol *member = scope->memberAt(i); Function *fun = member->type()->asFunctionType(); - if (!fun) + if (!fun || fun->isGenerated()) continue; if (wantSignals && !fun->isSignal()) continue; - else if (!wantSignals && !fun->isSlot()) + else if (!wantSignals && type == CompleteQt4Slots && !fun->isSlot()) continue; unsigned count = fun->argumentCount(); while (true) { - QString signature; - signature += Overview().prettyName(fun->name()); - signature += QLatin1Char('('); - for (unsigned i = 0; i < count; ++i) { - Symbol *arg = fun->argumentAt(i); - if (isQPrivateSignal(arg)) - continue; - if (i != 0) - signature += QLatin1Char(','); - signature += o.prettyType(arg->type()); - } - signature += QLatin1Char(')'); + const QString completionText = wantQt5SignalOrSlot + ? createQt5SignalOrSlot(fun, klass) + : createQt4SignalOrSlot(fun, o); - const QByteArray normalized = - QMetaObject::normalizedSignature(signature.toUtf8()); - - signature = QString::fromUtf8(normalized, normalized.size()); - - if (!signatures.contains(signature)) { + if (!signatures.contains(completionText)) { AssistProposalItem *ci = toCompletionItem(fun); if (!ci) break; - signatures.insert(signature); - ci->setText(signature); // fix the completion item. + signatures.insert(completionText); + ci->setText(completionText); // fix the completion item. m_completions.append(ci); } diff --git a/src/plugins/cpptools/cppcompletionassist.h b/src/plugins/cpptools/cppcompletionassist.h index e95c21603d3..8a31f9387d9 100644 --- a/src/plugins/cpptools/cppcompletionassist.h +++ b/src/plugins/cpptools/cppcompletionassist.h @@ -132,11 +132,13 @@ private: void completeNamespace(CPlusPlus::ClassOrNamespace *binding); void completeClass(CPlusPlus::ClassOrNamespace *b, bool staticLookup = true); void addClassMembersToCompletion(CPlusPlus::Scope *scope, bool staticLookup); - bool completeQtMethod(const QList &results, bool wantSignals); - bool completeSignal(const QList &results) - { return completeQtMethod(results, true); } - bool completeSlot(const QList &results) - { return completeQtMethod(results, false); } + enum CompleteQtMethodMode { + CompleteQt4Signals, + CompleteQt4Slots, + CompleteQt5Signals, + CompleteQt5Slots, + }; + bool completeQtMethod(const QList &results, CompleteQtMethodMode type); void globalCompletion(CPlusPlus::Scope *scope); void addCompletionItem(const QString &text, @@ -152,6 +154,10 @@ private: QSet *processed, QSet *definedMacros); + enum { + CompleteQt5SignalTrigger = CPlusPlus::T_LAST_TOKEN + 1, + CompleteQtSlotTrigger + }; CPlusPlus::LanguageFeatures m_languageFeatures; QScopedPointer m_interface; QScopedPointer m_model; diff --git a/src/plugins/cpptools/cppcompletionassistprovider.cpp b/src/plugins/cpptools/cppcompletionassistprovider.cpp index 2983fa27d4b..9b878aa9cf6 100644 --- a/src/plugins/cpptools/cppcompletionassistprovider.cpp +++ b/src/plugins/cpptools/cppcompletionassistprovider.cpp @@ -57,7 +57,7 @@ bool CppCompletionAssistProvider::isActivationCharSequence(const QString &sequen const QChar &ch = sequence.at(2); const QChar &ch2 = sequence.at(1); const QChar &ch3 = sequence.at(0); - if (activationSequenceChar(ch, ch2, ch3, 0, true) != 0) + if (activationSequenceChar(ch, ch2, ch3, 0, true, false) != 0) return true; return false; } @@ -71,7 +71,8 @@ int CppCompletionAssistProvider::activationSequenceChar(const QChar &ch, const QChar &ch2, const QChar &ch3, unsigned *kind, - bool wantFunctionCall) + bool wantFunctionCall, + bool wantQt5SignalSlots) { int referencePosition = 0; int completionKind = T_EOF_SYMBOL; @@ -136,6 +137,12 @@ int CppCompletionAssistProvider::activationSequenceChar(const QChar &ch, completionKind = T_POUND; referencePosition = 1; break; + case '&': + if (wantQt5SignalSlots) { + completionKind = T_AMPER; + referencePosition = 1; + } + break; } if (kind) diff --git a/src/plugins/cpptools/cppcompletionassistprovider.h b/src/plugins/cpptools/cppcompletionassistprovider.h index 944214968bd..fbc86bf2c10 100644 --- a/src/plugins/cpptools/cppcompletionassistprovider.h +++ b/src/plugins/cpptools/cppcompletionassistprovider.h @@ -65,7 +65,7 @@ public: static int activationSequenceChar(const QChar &ch, const QChar &ch2, const QChar &ch3, unsigned *kind, - bool wantFunctionCall); + bool wantFunctionCall, bool wantQt5SignalSlots); }; } // namespace CppTools