diff --git a/src/libs/cplusplus/FindUsages.cpp b/src/libs/cplusplus/FindUsages.cpp index 0c317520b5b..5443a810f38 100644 --- a/src/libs/cplusplus/FindUsages.cpp +++ b/src/libs/cplusplus/FindUsages.cpp @@ -226,6 +226,7 @@ public: continue; } if (const auto declarator = (*it)->asDeclarator()) { + Usage::Tags tags; if (containsToken(declarator->core_declarator)) { if (declarator->initializer && declarator->equal_token && (!declarator->postfix_declarator_list @@ -233,17 +234,47 @@ public: || !declarator->postfix_declarator_list->value->asFunctionDeclarator())) { return {Usage::Tag::Declaration, Usage::Tag::Write}; } - return Usage::Tag::Declaration; - } - if (const auto decl = (*(it + 1))->asSimpleDeclaration()) { - if (decl->symbols && decl->symbols->value) { - return checkPotentialWrite( - getTagsFromLhsAndRhs(decl->symbols->value->type(), - declarator->initializer, it + 1), - it + 1); + tags = Usage::Tag::Declaration; + if (declarator->postfix_declarator_list + && declarator->postfix_declarator_list->value) { + if (const FunctionDeclaratorAST * const funcDecl = declarator + ->postfix_declarator_list->value->asFunctionDeclarator()) { + for (SpecifierListAST *iter = funcDecl->cv_qualifier_list; iter; + iter = iter->next) { + if (!iter->value) + continue; + if (const auto simpleSpec = iter->value->asSimpleSpecifier(); + simpleSpec && simpleSpec->specifier_token) { + const Control * const ctl = m_findUsages->control(); + const Identifier * const id = m_findUsages->translationUnit() + ->tokenAt(simpleSpec->specifier_token).identifier; + if (id && (id->equalTo(ctl->cpp11Override()) + || id->equalTo(ctl->cpp11Final()))) { + tags |= Usage::Tag::Override; + break; + } + } + } + } } } - return {}; + if (const auto decl = (*(it + 1))->asSimpleDeclaration()) { + if (tags.toInt() && decl->qt_invokable_token) + return tags |= Usage::Tag::MocInvokable; + if (decl->symbols && decl->symbols->value) { + if (!tags) { + return checkPotentialWrite( + getTagsFromLhsAndRhs(decl->symbols->value->type(), + declarator->initializer, it + 1), + it + 1); + } + if (const auto func = decl->symbols->value->type()->asFunctionType()) { + if (func->isSignal() || func->isSlot() || func->isInvokable()) + return tags |= Usage::Tag::MocInvokable; + } + } + } + return tags; } if (const auto retStmt = (*it)->asReturnStatement()) { for (auto funcIt = it + 1; funcIt != m_astPath.rend(); ++funcIt) { diff --git a/src/libs/cplusplus/FindUsages.h b/src/libs/cplusplus/FindUsages.h index 79c31fe86d7..297bac0ca7e 100644 --- a/src/libs/cplusplus/FindUsages.h +++ b/src/libs/cplusplus/FindUsages.h @@ -22,6 +22,8 @@ public: Read = 1 << 1, Write = 1 << 2, WritableRef = 1 << 3, + Override = 1 << 4, + MocInvokable = 1 << 5, }; using Tags = QFlags; diff --git a/src/plugins/clangcodemodel/clangdfindreferences.cpp b/src/plugins/clangcodemodel/clangdfindreferences.cpp index ac369c92c89..42a6204e136 100644 --- a/src/plugins/clangcodemodel/clangdfindreferences.cpp +++ b/src/plugins/clangcodemodel/clangdfindreferences.cpp @@ -397,7 +397,17 @@ static Usage::Tags getUsageType(const ClangdAstPath &path) return Usage::Tag::WritableRef; return Usage::Tag::Read; } - return Usage::Tag::Declaration; + Usage::Tags tags = Usage::Tag::Declaration; + const auto children = pathIt->children().value_or(QList()); + for (const ClangdAstNode &child : children) { + if (child.role() == "attribute") { + if (child.kind() == "Override" || child.kind() == "Final") + tags |= Usage::Tag::Override; + else if (child.kind() == "Annotate" && child.arcanaContains("qt_")) + tags |= Usage::Tag::MocInvokable; + } + } + return tags; } if (pathIt->kind() == "MemberInitializer") return pathIt == path.rbegin() ? Usage::Tag::Write : Usage::Tag::Read; diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index 31bc731d23c..21df3efcea5 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -219,7 +219,7 @@ void ClangdTestFindReferences::test_data() makeItem(5, 21, Initialization), makeItem(45, 16, Usage::Tag::Read)}; ItemList pureVirtualRefs{makeItem(17, 17, Usage::Tag::Declaration), - makeItem(21, 9, Usage::Tag::Declaration)}; + makeItem(21, 9, {Usage::Tag::Declaration, Usage::Tag::Override})}; QTest::newRow("pure virtual declaration") << "defs.h" << 420 << pureVirtualRefs; QTest::newRow("pointer variable") << "main.cpp" << 52 << ItemList{ diff --git a/tests/auto/cplusplus/findusages/tst_findusages.cpp b/tests/auto/cplusplus/findusages/tst_findusages.cpp index 776f9d7000f..5eea5d1d474 100644 --- a/tests/auto/cplusplus/findusages/tst_findusages.cpp +++ b/tests/auto/cplusplus/findusages/tst_findusages.cpp @@ -116,6 +116,8 @@ private Q_SLOTS: void variadicMacros(); void writableRefs(); + void mocInvokables(); + void virtualOverride(); }; void tst_FindUsages::dump(const QList &usages) const @@ -724,7 +726,16 @@ void tst_FindUsages::qproperty_1() QCOMPARE(findUsages.usages().size(), 2); QCOMPARE(findUsages.usages().at(0).tags, Usage::Tags()); QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Declaration); - QCOMPARE(findUsages.references().size(), 2); + + Declaration *xChangedSignal = tst->memberAt(3)->asDeclaration(); + QVERIFY(xChangedSignal); + QCOMPARE(xChangedSignal->identifier()->chars(), "xChanged"); + findUsages(xChangedSignal); + QCOMPARE(findUsages.usages().size(), 3); + QCOMPARE(findUsages.usages().at(0).tags, Usage::Tags()); + QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags()); + QCOMPARE(findUsages.usages().at(2).tags, + (Usage::Tags{Usage::Tag::Declaration, Usage::Tag::MocInvokable})); } void tst_FindUsages::instantiateTemplateWithNestedClass() @@ -2343,5 +2354,137 @@ int main() QCOMPARE(find.usages().at(2).tags, Usage::Tag::Read); } +void tst_FindUsages::mocInvokables() +{ + const QByteArray src = + R"( +class O : public QObject { +public: + void aPublicFunction(); + Q_SLOT void aSlot(); + Q_SIGNAL void aSignal(); + Q_INVOKABLE void anInvokable(); +public slots: + void anotherSlot(); +signals: + void anotherSignal(); +private slots: + void yetAnotherSlot(); +private: + void aPrivateFunction(); +}; +)"; + + Document::Ptr doc = Document::create("mocInvokables"); + doc->setUtf8Source(src); + doc->parse(); + doc->check(); + + QVERIFY(doc->diagnosticMessages().isEmpty()); + QVERIFY(doc->globalSymbolCount() >= 1); + + Snapshot snapshot; + snapshot.insert(doc); + + Class *s = doc->globalSymbolAt(0)->asClass(); + QVERIFY(s); + QCOMPARE(s->name()->identifier()->chars(), "O"); + QCOMPARE(s->memberCount(), 8); + + const Usage::Tags invokable{Usage::Tag::Declaration, Usage::Tag::MocInvokable}; + + Declaration *sv = s->memberAt(0)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "aPublicFunction"); + FindUsages find(src, doc, snapshot, true); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration); + + sv = s->memberAt(1)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "aSlot"); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, invokable); + + sv = s->memberAt(2)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "aSignal"); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, invokable); + + sv = s->memberAt(3)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "anInvokable"); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, invokable); + + sv = s->memberAt(4)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "anotherSlot"); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, invokable); + + sv = s->memberAt(5)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "anotherSignal"); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, invokable); + + sv = s->memberAt(6)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "yetAnotherSlot"); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, invokable); + + sv = s->memberAt(7)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "aPrivateFunction"); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration); +} + +void tst_FindUsages::virtualOverride() +{ + const QByteArray src = + R"( +struct Base { virtual void foo(); }; +struct Derived : public Base { void foo() override; }; +)"; + + Document::Ptr doc = Document::create("virtualOverride"); + doc->setUtf8Source(src); + doc->parse(); + doc->check(); + + QVERIFY(doc->diagnosticMessages().isEmpty()); + QVERIFY(doc->globalSymbolCount() >= 2); + + Snapshot snapshot; + snapshot.insert(doc); + + Class *s = doc->globalSymbolAt(1)->asClass(); + QVERIFY(s); + QCOMPARE(s->name()->identifier()->chars(), "Derived"); + QCOMPARE(s->memberCount(), 1); + + Declaration *sv = s->memberAt(0)->asDeclaration(); + QVERIFY(sv); + QCOMPARE(sv->name()->identifier()->chars(), "foo"); + + FindUsages find(src, doc, snapshot, true); + find(sv); + QCOMPARE(find.usages().size(), 1); + QCOMPARE(find.usages().at(0).tags, + (Usage::Tags{Usage::Tag::Declaration, Usage::Tag::Override})); +} + QTEST_APPLESS_MAIN(tst_FindUsages) #include "tst_findusages.moc"