CPlusPlus: Add more usage tags

To be used in subsequent patches.

Change-Id: Id7140aa39bb2adba343cc12b0273c90f3c12abeb
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Christian Kandeler
2022-11-02 14:27:17 +01:00
parent d891e18edc
commit 3e3569f6dc
5 changed files with 198 additions and 12 deletions

View File

@@ -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) {

View File

@@ -22,6 +22,8 @@ public:
Read = 1 << 1,
Write = 1 << 2,
WritableRef = 1 << 3,
Override = 1 << 4,
MocInvokable = 1 << 5,
};
using Tags = QFlags<Tag>;

View File

@@ -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<ClangdAstNode>());
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;

View File

@@ -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{

View File

@@ -116,6 +116,8 @@ private Q_SLOTS:
void variadicMacros();
void writableRefs();
void mocInvokables();
void virtualOverride();
};
void tst_FindUsages::dump(const QList<Usage> &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"