Files
qt-creator/tests/auto/cplusplus/findusages/tst_findusages.cpp
Kai Köhne 56baf8c058 Remove GPL-3.0+ from license identifiers
Since we also license under GPL-3.0 WITH Qt-GPL-exception-1.0,
this applies only to a hypothetical newer version of GPL, that doesn't
exist yet. If such a version emerges, we can still decide to relicense...

While at it, replace (deprecated) GPL-3.0 with more explicit GPL-3.0-only

Change was done by running

  find . -type f -exec perl -pi -e "s/LicenseRef-Qt-Commercial OR GPL-3.0\+ OR GPL-3.0 WITH Qt-GPL-exception-1.0/LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0/g" {} \;

Change-Id: I5097e6ce8d10233993ee30d7e25120e2659eb10b
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
2023-01-06 11:15:13 +00:00

2493 lines
78 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "../cplusplus_global.h"
#include <QtTest>
#include <QObject>
#include <QList>
#include <cplusplus/AST.h>
#include <cplusplus/ASTVisitor.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/FindUsages.h>
#include <cplusplus/Literals.h>
#include <cplusplus/LookupContext.h>
#include <cplusplus/Name.h>
#include <cplusplus/Overview.h>
#include <cplusplus/ResolveExpression.h>
#include <cplusplus/Symbols.h>
#include <cplusplus/TranslationUnit.h>
//TESTED_COMPONENT=src/libs/cplusplus
using namespace CPlusPlus;
class CollectNames: public ASTVisitor
{
public:
CollectNames(TranslationUnit *xUnit): ASTVisitor(xUnit) {}
QList<NameAST*> operator()(const char *name) {
_name = name;
_exprs.clear();
accept(translationUnit()->ast());
return _exprs;
}
virtual bool preVisit(AST *ast) {
if (NameAST *nameAst = ast->asName())
if (!qstrcmp(_name, nameAst->name->identifier()->chars()))
_exprs.append(nameAst);
return true;
}
private:
QList<NameAST*> _exprs;
const char *_name;
};
const Usage::Tags Initialization{Usage::Tag::Declaration, Usage::Tag::Write};
class tst_FindUsages: public QObject
{
Q_OBJECT
private:
void dump(const QList<Usage> &usages) const;
private Q_SLOTS:
void inlineMethod();
void lambdaCaptureByValue();
void lambdaCaptureByReference();
void shadowedNames_1();
void shadowedNames_2();
void staticVariables();
void structuredBinding();
void functionNameFoundInArguments();
void memberFunctionFalsePositives_QTCREATORBUG2176();
void resolveTemplateConstructor();
void templateConstructorVsCallOperator();
// Qt keywords
void qproperty_1();
// Objective-C
void objc_args();
// void objc_methods();
// void objc_fields();
// void objc_classes();
// templates
void instantiateTemplateWithNestedClass();
void operatorAsteriskOfNestedClassOfTemplateClass_QTCREATORBUG9006();
void operatorArrowOfNestedClassOfTemplateClass_QTCREATORBUG9005();
void templateClassParameters();
void templateClass_className();
void templateFunctionParameters();
void anonymousClass_QTCREATORBUG8963();
void anonymousClass_QTCREATORBUG11859();
void using_insideGlobalNamespace();
void using_insideNamespace();
void using_insideFunction();
void templatedFunction_QTCREATORBUG9749();
void templateInNamespaceTypeOutside();
void usingInDifferentNamespace_QTCREATORBUG7978();
void unicodeIdentifier();
void inAlignas();
void memberAccessAsTemplate();
void variadicFunctionTemplate();
void typeTemplateParameterWithDefault();
void resolveOrder_for_templateFunction_vs_function();
void templateArrowOperator_with_defaultType();
void templateSpecialization_with_IntArgument();
void templateSpecialization_with_BoolArgument();
void templatePartialSpecialization();
void templatePartialSpecialization_2();
void template_SFINAE_1();
void variableTemplateInExpression();
void variadicMacros();
void writableRefs();
void mocInvokables();
void virtualOverride();
};
void tst_FindUsages::dump(const QList<Usage> &usages) const
{
QTextStream err(stderr, QIODevice::WriteOnly);
err << "DEBUG : " << usages.size() << " usages:" << Qt::endl;
for (const Usage &usage : usages) {
err << "DEBUG : " << usage.path << ":" << usage.line << ":" << usage.col << ":"
<< usage.len << ":" << usage.lineText << Qt::endl;
}
}
void tst_FindUsages::inlineMethod()
{
const QByteArray src = "\n"
"class Tst {\n"
" int method(int arg) {\n"
" return arg;\n"
" }\n"
"};\n";
Document::Ptr doc = Document::create("inlineMethod");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 1);
Snapshot snapshot;
snapshot.insert(doc);
Class *tst = doc->globalSymbolAt(0)->asClass();
QVERIFY(tst);
QCOMPARE(tst->memberCount(), 1);
Function *method = tst->memberAt(0)->asFunction();
QVERIFY(method);
QCOMPARE(method->argumentCount(), 1);
Argument *arg = method->argumentAt(0)->asArgument();
QVERIFY(arg);
QCOMPARE(arg->identifier()->chars(), "arg");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(arg);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
QCOMPARE(findUsages.references().size(), 2);
}
void tst_FindUsages::lambdaCaptureByValue()
{
const QByteArray src = "\n"
"void f() {\n"
" int test;\n"
" [test] { ++test; };\n"
"}\n";
Document::Ptr doc = Document::create("lambdaCaptureByValue");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 1);
Snapshot snapshot;
snapshot.insert(doc);
Function *f = doc->globalSymbolAt(0)->asFunction();
QVERIFY(f);
QCOMPARE(f->memberCount(), 1);
Block *b = f->memberAt(0)->asBlock();
QCOMPARE(b->memberCount(), 2);
Declaration *d = b->memberAt(0)->asDeclaration();
QVERIFY(d);
QCOMPARE(d->name()->identifier()->chars(), "test");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(d);
QCOMPARE(findUsages.usages().size(), 3);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tag::Read);
}
void tst_FindUsages::lambdaCaptureByReference()
{
const QByteArray src = "\n"
"void f() {\n"
" int test;\n"
" [&test] { ++test; };\n"
"}\n";
Document::Ptr doc = Document::create("lambdaCaptureByReference");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 1);
Snapshot snapshot;
snapshot.insert(doc);
Function *f = doc->globalSymbolAt(0)->asFunction();
QVERIFY(f);
QCOMPARE(f->memberCount(), 1);
Block *b = f->memberAt(0)->asBlock();
QCOMPARE(b->memberCount(), 2);
Declaration *d = b->memberAt(0)->asDeclaration();
QVERIFY(d);
QCOMPARE(d->name()->identifier()->chars(), "test");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(d);
QCOMPARE(findUsages.usages().size(), 3);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
QEXPECT_FAIL(nullptr, "parser does not record capture type", Continue);
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tag::Write);
}
void tst_FindUsages::shadowedNames_1()
{
const QByteArray src = "\n"
"int a();\n"
"struct X{ int a(); };\n"
"int X::a() {}\n"
"void f(X x) { x.a(); }\n"
"void g() { a(); }\n"
;
Document::Ptr doc = Document::create("shadowedNames_1");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 5);
Snapshot snapshot;
snapshot.insert(doc);
Declaration *d = doc->globalSymbolAt(0)->asDeclaration();
QVERIFY(d);
QCOMPARE(d->name()->identifier()->chars(), "a");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(d);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
}
void tst_FindUsages::shadowedNames_2()
{
const QByteArray src = "\n"
"int a();\n"
"struct X{ int a(); };\n"
"int X::a() {}\n"
"void f(X x) { x.a(); }\n"
"void g() { a(); }\n";
Document::Ptr doc = Document::create("shadowedNames_2");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 5);
Snapshot snapshot;
snapshot.insert(doc);
Class *c = doc->globalSymbolAt(1)->asClass();
QVERIFY(c);
QCOMPARE(c->name()->identifier()->chars(), "X");
QCOMPARE(c->memberCount(), 1);
Declaration *d = c->memberAt(0)->asDeclaration();
QVERIFY(d);
QCOMPARE(d->name()->identifier()->chars(), "a");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(d);
QCOMPARE(findUsages.usages().size(), 3);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tags());
}
void tst_FindUsages::staticVariables()
{
const QByteArray src = "\n"
"struct Outer\n"
"{\n"
" static int Foo;\n"
" struct Inner\n"
" {\n"
" Outer *outer;\n"
" void foo();\n"
" };\n"
"};\n"
"\n"
"int Outer::Foo = 42;\n"
"\n"
"void Outer::Inner::foo()\n"
"{\n"
" Foo = 7;\n"
" Outer::Foo = 7;\n"
" outer->Foo = 7;\n"
"}\n"
;
Document::Ptr doc = Document::create("staticVariables");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 3);
Snapshot snapshot;
snapshot.insert(doc);
Class *c = doc->globalSymbolAt(0)->asClass();
QVERIFY(c);
QCOMPARE(c->name()->identifier()->chars(), "Outer");
QCOMPARE(c->memberCount(), 2);
Declaration *d = c->memberAt(0)->asDeclaration();
QVERIFY(d);
QCOMPARE(d->name()->identifier()->chars(), "Foo");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(d);
QCOMPARE(findUsages.usages().size(), 5);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Initialization);
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tag::Write);
QCOMPARE(findUsages.usages().at(3).tags, Usage::Tag::Write);
QCOMPARE(findUsages.usages().at(4).tags, Usage::Tag::Write);
}
void tst_FindUsages::structuredBinding()
{
const QByteArray src = "\n"
"int array[] = {1, 2};\n"
"const auto &[e1, e2] = array;\n"
"const int i1 = e1;\n"
"const int i2 = e2;\n"
;
Document::Ptr doc = Document::create("structuredBinding");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 5);
Snapshot snapshot;
snapshot.insert(doc);
Declaration * const d1 = doc->globalSymbolAt(1)->asDeclaration();
QVERIFY(d1);
Declaration * const d2 = doc->globalSymbolAt(2)->asDeclaration();
QVERIFY(d2);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(d1);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Initialization);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
findUsages(d2);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Initialization);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::functionNameFoundInArguments()
{
const QByteArray src =
R"(
void bar(); // call find usages for bar from here. This is 1st result
void foo(int bar); // should not be found
void foo(int bar){} // should not be found
void foo2(int b=bar()); // 2nd result
void foo2(int b=bar()){} // 3rd result
)";
Document::Ptr doc = Document::create("functionNameFoundInArguments");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount() >= 1);
Symbol *s = doc->globalSymbolAt(0);
QCOMPARE(s->name()->identifier()->chars(), "bar");
Snapshot snapshot;
snapshot.insert(doc);
FindUsages find(src, doc, snapshot, true);
find(s);
QCOMPARE(find.usages().size(), 3);
QCOMPARE(find.usages()[0].tags, Usage::Tag::Declaration);
QCOMPARE(find.usages()[0].line, 1);
QCOMPARE(find.usages()[0].col, 5);
QCOMPARE(find.usages()[1].tags, Usage::Tags());
QCOMPARE(find.usages()[1].line, 4);
QCOMPARE(find.usages()[1].col, 16);
QCOMPARE(find.usages()[2].tags, Usage::Tags());
QCOMPARE(find.usages()[2].line, 5);
QCOMPARE(find.usages()[2].col, 16);
}
void tst_FindUsages::memberFunctionFalsePositives_QTCREATORBUG2176()
{
const QByteArray src =
R"(
void otherFunction(int value){}
struct Struct{
static int foo(){return 1;}
int bar(){
int foo=Struct::foo();
otherFunction(foo);
}
};
)";
Document::Ptr doc = Document::create("memberFunctionFalsePositives");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 2);
Class *s = doc->globalSymbolAt(1)->asClass();
QVERIFY(s);
QCOMPARE(s->name()->identifier()->chars(), "Struct");
QCOMPARE(s->memberCount(), 2);
Symbol *memberFunctionFoo = s->memberAt(0);
QVERIFY(memberFunctionFoo);
QCOMPARE(memberFunctionFoo->name()->identifier()->chars(), "foo");
QVERIFY(memberFunctionFoo->asFunction());
Function* bar = s->memberAt(1)->asFunction();
QVERIFY(bar);
QCOMPARE(bar->name()->identifier()->chars(), "bar");
QCOMPARE(bar->memberCount(), 1);
Block* block = bar->memberAt(0)->asBlock();
QVERIFY(block);
QCOMPARE(block->memberCount(), 1);
Symbol *variableFoo = block->memberAt(0);
QVERIFY(variableFoo);
QCOMPARE(variableFoo->name()->identifier()->chars(), "foo");
QVERIFY(variableFoo->asDeclaration());
Snapshot snapshot;
snapshot.insert(doc);
FindUsages find(src, doc, snapshot, true);
find(memberFunctionFoo);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Usage::Tag::Declaration);
QCOMPARE(find.usages()[0].line, 3);
QCOMPARE(find.usages()[0].col, 15);
QCOMPARE(find.usages()[1].tags, Usage::Tags());
QCOMPARE(find.usages()[1].line, 5);
QCOMPARE(find.usages()[1].col, 24);
find(variableFoo);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Initialization);
QCOMPARE(find.usages()[0].line, 5);
QCOMPARE(find.usages()[0].col, 12);
QCOMPARE(find.usages()[1].tags, Usage::Tag::Read);
QCOMPARE(find.usages()[1].line, 6);
QCOMPARE(find.usages()[1].col, 22);
}
void tst_FindUsages::resolveTemplateConstructor()
{
const QByteArray src =
R"(
struct MyStruct { int value; };
template <class T> struct Tmp {
T str;
};
template <class T> struct Tmp2 {
Tmp2(){}
T str;
};
template <class T> struct Tmp3 {
Tmp3(int i){}
T str;
};
int main() {
auto tmp = Tmp<MyStruct>();
auto tmp2 = Tmp2<MyStruct>();
auto tmp3 = Tmp3<MyStruct>(1);
tmp.str.value;
tmp2.str.value;
tmp3.str.value;
Tmp<MyStruct>().str.value;
Tmp2<MyStruct>().str.value;
Tmp3<MyStruct>(1).str.value;
}
)";
Document::Ptr doc = Document::create("resolveTemplateConstructor");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount() == 5);
Class *s = doc->globalSymbolAt(0)->asClass();
QVERIFY(s);
QCOMPARE(s->name()->identifier()->chars(), "MyStruct");
QCOMPARE(s->memberCount(), 1);
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
Snapshot snapshot;
snapshot.insert(doc);
FindUsages find(src, doc, snapshot, true);
find(sv);
QCOMPARE(find.usages().size(), 7);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(2).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(3).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(4).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(5).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(6).tags, Usage::Tag::Read);
}
void tst_FindUsages::templateConstructorVsCallOperator()
{
const QByteArray src =
R"(
struct MyStruct { int value; };
template<class T> struct Tmp {
T str;
MyStruct operator()() { return MyStruct(); }
};
struct Simple {
MyStruct str;
MyStruct operator()() { return MyStruct(); }
};
int main()
{
Tmp<MyStruct>().str.value;
Tmp<MyStruct>()().value;
Tmp<MyStruct> t;
t().value;
Simple().str.value;
Simple()().value;
Simple s;
s().value;
}
)";
Document::Ptr doc = Document::create("resolveTemplateConstructor");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount() == 4);
Class *s = doc->globalSymbolAt(0)->asClass();
QVERIFY(s);
QCOMPARE(s->name()->identifier()->chars(), "MyStruct");
QCOMPARE(s->memberCount(), 1);
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
Snapshot snapshot;
snapshot.insert(doc);
FindUsages find(src, doc, snapshot, true);
find(sv);
QCOMPARE(find.usages().size(), 7);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(2).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(3).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(4).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(5).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(6).tags, Usage::Tag::Read);
}
#if 0
@interface Clazz {} +(void)method:(int)arg; @end
@implementation Clazz +(void)method:(int)arg {
[Clazz method:arg];
}
@end
#endif
const QByteArray objcSource = "\n"
"@interface Clazz {} +(void)method:(int)arg; @end\n"
"@implementation Clazz +(void)method:(int)arg {\n"
" [Clazz method:arg];\n"
"}\n"
"@end\n";
void tst_FindUsages::objc_args()
{
Document::Ptr doc = Document::create("objc_args");
doc->setUtf8Source(objcSource);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 2);
Snapshot snapshot;
snapshot.insert(doc);
TranslationUnit *xUnit = doc->translationUnit();
QList<NameAST*>exprs = CollectNames(xUnit)("arg");
QCOMPARE(exprs.size(), 3);
ObjCClass *iface = doc->globalSymbolAt(0)->asObjCClass();
QVERIFY(iface);
QVERIFY(iface->isInterface());
QCOMPARE(iface->memberCount(), 1);
Declaration *methodIface = iface->memberAt(0)->asDeclaration();
QVERIFY(methodIface);
QCOMPARE(methodIface->identifier()->chars(), "method");
QVERIFY(methodIface->type()->asObjCMethodType());
ObjCClass *impl = doc->globalSymbolAt(1)->asObjCClass();
QVERIFY(impl);
QVERIFY(!impl->isInterface());
QCOMPARE(impl->memberCount(), 1);
ObjCMethod *methodImpl = impl->memberAt(0)->asObjCMethod();
QVERIFY(methodImpl);
QCOMPARE(methodImpl->identifier()->chars(), "method");
QCOMPARE(methodImpl->argumentCount(), 1);
Argument *arg = methodImpl->argumentAt(0)->asArgument();
QCOMPARE(arg->identifier()->chars(), "arg");
FindUsages findUsages(objcSource, doc, snapshot, true);
findUsages(arg);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.references().size(), 2);
}
void tst_FindUsages::qproperty_1()
{
const QByteArray src = "\n"
"class Tst: public QObject {\n"
" Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged)\n"
"public:\n"
" int x() { return _x; }\n"
" void setX(int x) { if (_x != x) { _x = x; emit xChanged(x); } }\n"
"signals:\n"
" void xChanged(int);\n"
"private:\n"
" int _x;\n"
"};\n";
Document::Ptr doc = Document::create("qproperty_1");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 1);
Snapshot snapshot;
snapshot.insert(doc);
Class *tst = doc->globalSymbolAt(0)->asClass();
QVERIFY(tst);
QCOMPARE(tst->memberCount(), 5);
Function *setX_method = tst->memberAt(2)->asFunction();
QVERIFY(setX_method);
QCOMPARE(setX_method->identifier()->chars(), "setX");
QCOMPARE(setX_method->argumentCount(), 1);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(setX_method);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Declaration);
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()
{
const QByteArray src = "\n"
"struct Foo\n"
"{ int bar; };\n"
"template <typename T>\n"
"struct Template\n"
"{\n"
" struct Nested\n"
" {\n"
" T t;\n"
" }nested;\n"
"};\n"
"void f()\n"
"{\n"
" Template<Foo> templateFoo;\n"
" templateFoo.nested.t.bar;\n"
"}\n"
;
Document::Ptr doc = Document::create("simpleTemplate");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 3);
Snapshot snapshot;
snapshot.insert(doc);
Class *classFoo = doc->globalSymbolAt(0)->asClass();
QVERIFY(classFoo);
QCOMPARE(classFoo->memberCount(), 1);
Declaration *barDeclaration = classFoo->memberAt(0)->asDeclaration();
QVERIFY(barDeclaration);
QCOMPARE(barDeclaration->name()->identifier()->chars(), "bar");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(barDeclaration);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::operatorAsteriskOfNestedClassOfTemplateClass_QTCREATORBUG9006()
{
const QByteArray src = "\n"
"struct Foo { int foo; };\n"
"\n"
"template<class T>\n"
"struct Outer\n"
"{\n"
" struct Nested\n"
" {\n"
" const T &operator*() { return t; }\n"
" T t;\n"
" };\n"
"};\n"
"\n"
"void bug()\n"
"{\n"
" Outer<Foo>::Nested nested;\n"
" (*nested).foo;\n"
"}\n"
;
Document::Ptr doc = Document::create("operatorAsteriskOfNestedClassOfTemplateClass_QTCREATORBUG9006");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 3);
Snapshot snapshot;
snapshot.insert(doc);
Class *classFoo = doc->globalSymbolAt(0)->asClass();
QVERIFY(classFoo);
QCOMPARE(classFoo->memberCount(), 1);
Declaration *fooDeclaration = classFoo->memberAt(0)->asDeclaration();
QVERIFY(fooDeclaration);
QCOMPARE(fooDeclaration->name()->identifier()->chars(), "foo");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(fooDeclaration);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::anonymousClass_QTCREATORBUG8963()
{
const QByteArray src =
"typedef enum {\n"
" FIRST\n"
"} isNotInt;\n"
"typedef struct {\n"
" int isint;\n"
" int isNotInt;\n"
"} Struct;\n"
"void foo()\n"
"{\n"
" Struct s;\n"
" s.isint;\n"
" s.isNotInt;\n"
" FIRST;\n"
"}\n"
;
Document::Ptr doc = Document::create("anonymousClass_QTCREATORBUG8963");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 5);
Snapshot snapshot;
snapshot.insert(doc);
Class *structSymbol = doc->globalSymbolAt(2)->asClass();
QVERIFY(structSymbol);
QCOMPARE(structSymbol->memberCount(), 2);
Declaration *isNotIntDeclaration = structSymbol->memberAt(1)->asDeclaration();
QVERIFY(isNotIntDeclaration);
QCOMPARE(isNotIntDeclaration->name()->identifier()->chars(), "isNotInt");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(isNotIntDeclaration);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::anonymousClass_QTCREATORBUG11859()
{
const QByteArray src =
"struct Foo {\n"
"};\n"
"typedef struct {\n"
" int Foo;\n"
"} Struct;\n"
"void foo()\n"
"{\n"
" Struct s;\n"
" s.Foo;\n"
"}\n"
;
Document::Ptr doc = Document::create("anonymousClass_QTCREATORBUG11859");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 4);
Snapshot snapshot;
snapshot.insert(doc);
Class *fooAsStruct = doc->globalSymbolAt(0)->asClass();
QVERIFY(fooAsStruct);
Class *structSymbol = doc->globalSymbolAt(1)->asClass();
QVERIFY(structSymbol);
QCOMPARE(structSymbol->memberCount(), 1);
Declaration *fooAsMemberOfAnonymousStruct = structSymbol->memberAt(0)->asDeclaration();
QVERIFY(fooAsMemberOfAnonymousStruct);
QCOMPARE(fooAsMemberOfAnonymousStruct->name()->identifier()->chars(), "Foo");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(fooAsStruct);
QCOMPARE(findUsages.references().size(), 1);
QCOMPARE(findUsages.usages().size(), 1);
QCOMPARE(findUsages.usages().first().tags, Usage::Tag::Declaration);
findUsages(fooAsMemberOfAnonymousStruct);
QCOMPARE(findUsages.references().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::using_insideGlobalNamespace()
{
const QByteArray src =
"namespace NS\n"
"{\n"
"struct Struct\n"
"{\n"
" int bar;\n"
"};\n"
"}\n"
"using NS::Struct;\n"
"void foo()\n"
"{\n"
" Struct s;\n"
"}\n"
;
Document::Ptr doc = Document::create("using_insideGlobalNamespace");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 3);
Snapshot snapshot;
snapshot.insert(doc);
Namespace *nsSymbol = doc->globalSymbolAt(0)->asNamespace();
QVERIFY(nsSymbol);
QCOMPARE(nsSymbol->memberCount(), 1);
Class *structSymbol = nsSymbol->memberAt(0)->asClass();
QVERIFY(structSymbol);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(structSymbol);
QCOMPARE(findUsages.usages().size(), 3);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tags());
}
void tst_FindUsages::using_insideNamespace()
{
const QByteArray src =
"namespace NS\n"
"{\n"
"struct Struct\n"
"{\n"
" int bar;\n"
"};\n"
"}\n"
"namespace NS1\n"
"{\n"
"using NS::Struct;\n"
"void foo()\n"
"{\n"
" Struct s;\n"
"}\n"
"}\n"
;
Document::Ptr doc = Document::create("using_insideNamespace");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 2);
Snapshot snapshot;
snapshot.insert(doc);
Namespace *nsSymbol = doc->globalSymbolAt(0)->asNamespace();
QVERIFY(nsSymbol);
QCOMPARE(nsSymbol->memberCount(), 1);
Class *structSymbol = nsSymbol->memberAt(0)->asClass();
QVERIFY(structSymbol);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(structSymbol);
QCOMPARE(findUsages.usages().size(), 3);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tags());
}
void tst_FindUsages::using_insideFunction()
{
const QByteArray src =
"namespace NS\n"
"{\n"
"struct Struct\n"
"{\n"
" int bar;\n"
"};\n"
"}\n"
"void foo()\n"
"{\n"
" using NS::Struct;\n"
" Struct s;\n"
"}\n"
;
Document::Ptr doc = Document::create("using_insideFunction");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 2);
Snapshot snapshot;
snapshot.insert(doc);
Namespace *nsSymbol = doc->globalSymbolAt(0)->asNamespace();
QVERIFY(nsSymbol);
QCOMPARE(nsSymbol->memberCount(), 1);
Class *structSymbol = nsSymbol->memberAt(0)->asClass();
QVERIFY(structSymbol);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(structSymbol);
QCOMPARE(findUsages.usages().size(), 3);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tags());
}
void tst_FindUsages::operatorArrowOfNestedClassOfTemplateClass_QTCREATORBUG9005()
{
const QByteArray src = "\n"
"struct Foo { int foo; };\n"
"\n"
"template<class T>\n"
"struct Outer\n"
"{\n"
" struct Nested\n"
" {\n"
" T *operator->() { return 0; }\n"
" };\n"
"};\n"
"\n"
"void bug()\n"
"{\n"
" Outer<Foo>::Nested nested;\n"
" nested->foo;\n"
"}\n"
;
Document::Ptr doc = Document::create("operatorArrowOfNestedClassOfTemplateClass_QTCREATORBUG9005");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 3);
Snapshot snapshot;
snapshot.insert(doc);
Class *classFoo = doc->globalSymbolAt(0)->asClass();
QVERIFY(classFoo);
QCOMPARE(classFoo->memberCount(), 1);
Declaration *fooDeclaration = classFoo->memberAt(0)->asDeclaration();
QVERIFY(fooDeclaration);
QCOMPARE(fooDeclaration->name()->identifier()->chars(), "foo");
FindUsages findUsages(src, doc, snapshot, true);
findUsages(fooDeclaration);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::templateClassParameters()
{
const QByteArray src = "\n"
"template <class T>\n"
"struct TS\n"
"{\n"
" void set(T t) { T t1 = t; }\n"
" T get();\n"
" T t;\n"
"};\n"
;
Document::Ptr doc = Document::create("templateClassParameters");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 1);
Snapshot snapshot;
snapshot.insert(doc);
Template *templateClassTS = doc->globalSymbolAt(0)->asTemplate();
QVERIFY(templateClassTS);
QCOMPARE(templateClassTS->memberCount(), 2);
QCOMPARE(templateClassTS->templateParameterCount(), 1);
TypenameArgument *templArgument = templateClassTS->templateParameterAt(0)->asTypenameArgument();
QVERIFY(templArgument);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(templArgument);
QCOMPARE(findUsages.usages().size(), 5);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Template);
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(3).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(4).tags, Usage::Tags());
}
void tst_FindUsages::templateClass_className()
{
const QByteArray src = "\n"
"template <class T>\n"
"struct TS\n"
"{\n"
" TS();\n"
" ~TS();\n"
"};\n"
"template <class T>\n"
"TS<T>::TS()\n"
"{\n"
"}\n"
"template <class T>\n"
"TS<T>::~TS()\n"
"{\n"
"}\n"
;
Document::Ptr doc = Document::create("templateClass_className");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 3);
Snapshot snapshot;
snapshot.insert(doc);
Template *templateClassTS = doc->globalSymbolAt(0)->asTemplate();
QVERIFY(templateClassTS);
Class *classTS = templateClassTS->memberAt(1)->asClass();
QVERIFY(classTS);
QCOMPARE(classTS->memberCount(), 2);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(classTS);
QCOMPARE(findUsages.usages().size(), 7);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::ConstructorDestructor);
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tag::ConstructorDestructor);
QCOMPARE(findUsages.usages().at(3).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(4).tags, Usage::Tag::ConstructorDestructor);
QCOMPARE(findUsages.usages().at(5).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(6).tags, Usage::Tag::ConstructorDestructor);
}
void tst_FindUsages::templateFunctionParameters()
{
const QByteArray src = "\n"
"template<class T>\n"
"T foo(T t)\n"
"{\n"
" typename T;\n"
"}\n"
;
Document::Ptr doc = Document::create("templateFunctionParameters");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 1);
Snapshot snapshot;
snapshot.insert(doc);
Template *templateFunctionTS = doc->globalSymbolAt(0)->asTemplate();
QVERIFY(templateFunctionTS);
QCOMPARE(templateFunctionTS->memberCount(), 2);
QCOMPARE(templateFunctionTS->templateParameterCount(), 1);
TypenameArgument *templArgument = templateFunctionTS->templateParameterAt(0)->asTypenameArgument();
QVERIFY(templArgument);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(templArgument);
QCOMPARE(findUsages.usages().size(), 4);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tag::Template);
QCOMPARE(findUsages.usages().at(3).tags, Usage::Tags());
}
void tst_FindUsages::templatedFunction_QTCREATORBUG9749()
{
const QByteArray src = "\n"
"template <class IntType> char *reformatInteger(IntType value, int format) {}\n"
"void func(int code, int format) {\n"
" reformatInteger(code, format);"
"}\n"
;
Document::Ptr doc = Document::create("templatedFunction_QTCREATORBUG9749");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 2);
Snapshot snapshot;
snapshot.insert(doc);
Template *funcTempl = doc->globalSymbolAt(0)->asTemplate();
QVERIFY(funcTempl);
QCOMPARE(funcTempl->memberCount(), 2);
Function *func = funcTempl->memberAt(1)->asFunction();
FindUsages findUsages(src, doc, snapshot, true);
findUsages(func);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags,
(Usage::Tags{Usage::Tag::Declaration, Usage::Tag::Template}));
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
}
void tst_FindUsages::templateInNamespaceTypeOutside()
{
const QByteArray src = R"(
struct S { int v; };
namespace N {
template <typename T> struct Ptr {
T* operator->() { return {}; }
};
template<typename T> Ptr<T> makePtr() { return {}; }
}
int func()
{
auto s = N::makePtr<S>();
return s->v;
})";
Document::Ptr doc = Document::create("templateInNamespaceTypeOutside");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 3);
Snapshot snapshot;
snapshot.insert(doc);
Class * const structDecl = doc->globalSymbolAt(0)->asClass();
QVERIFY(structDecl);
QCOMPARE(structDecl->memberCount(), 1);
Symbol * const v = structDecl->memberAt(0);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(v);
QCOMPARE(findUsages.usages().size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::usingInDifferentNamespace_QTCREATORBUG7978()
{
const QByteArray src = "\n"
"struct S {};\n"
"namespace std\n"
"{\n"
" template <typename T> struct shared_ptr{};\n"
"}\n"
"namespace NS\n"
"{\n"
" using std::shared_ptr;\n"
"}\n"
"void fun()\n"
"{\n"
" NS::shared_ptr<S> p;\n"
"}\n"
;
Document::Ptr doc = Document::create("usingInDifferentNamespace_QTCREATORBUG7978");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 4);
Snapshot snapshot;
snapshot.insert(doc);
Namespace *ns = doc->globalSymbolAt(1)->asNamespace();
QVERIFY(ns);
QCOMPARE(ns->memberCount(), 1);
Template *templateClass = ns->memberAt(0)->asTemplate();
FindUsages findUsages(src, doc, snapshot, true);
findUsages(templateClass);
QCOMPARE(findUsages.usages().size(), 3);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tags());
QCOMPARE(findUsages.usages().at(2).tags, Usage::Tags());
}
void tst_FindUsages::unicodeIdentifier()
{
const QByteArray src = "\n"
"int var" TEST_UNICODE_IDENTIFIER ";\n"
"void f() { var" TEST_UNICODE_IDENTIFIER " = 1; }\n";
;
Document::Ptr doc = Document::create("u");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 2);
Snapshot snapshot;
snapshot.insert(doc);
Declaration *declaration = doc->globalSymbolAt(0)->asDeclaration();
QVERIFY(declaration);
FindUsages findUsages(src, doc, snapshot, true);
findUsages(declaration);
const QList<Usage> usages = findUsages.usages();
QCOMPARE(usages.size(), 2);
QCOMPARE(findUsages.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(findUsages.usages().at(1).tags, Usage::Tag::Write);
QCOMPARE(usages.at(0).len, 7);
QCOMPARE(usages.at(1).len, 7);
}
void tst_FindUsages::inAlignas()
{
const QByteArray src = "\n"
"struct One {};\n"
"struct alignas(One) Two {};\n"
;
Document::Ptr doc = Document::create("inAlignas");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 2);
Snapshot snapshot;
snapshot.insert(doc);
Class *c = doc->globalSymbolAt(0)->asClass();
QVERIFY(c);
QCOMPARE(c->name()->identifier()->chars(), "One");
FindUsages find(src, doc, snapshot, true);
find(c);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Usage::Tag::Declaration);
QCOMPARE(find.usages()[0].line, 1);
QCOMPARE(find.usages()[0].col, 7);
QCOMPARE(find.usages()[1].tags, Usage::Tags());
QCOMPARE(find.usages()[1].line, 2);
QCOMPARE(find.usages()[1].col, 15);
}
void tst_FindUsages::memberAccessAsTemplate()
{
const QByteArray src = "\n"
"struct Foo {};\n"
"struct Bar {\n"
" template <typename T>\n"
" T *templateFunc() { return 0; }\n"
"};\n"
"struct Test {\n"
" Bar member;\n"
" void testFunc();\n"
"};\n"
"void Test::testFunc() {\n"
" member.templateFunc<Foo>();\n"
"}\n";
Document::Ptr doc = Document::create("memberAccessAsTemplate");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 4);
Snapshot snapshot;
snapshot.insert(doc);
{ // Test "Foo"
Class *c = doc->globalSymbolAt(0)->asClass();
QVERIFY(c);
QCOMPARE(c->name()->identifier()->chars(), "Foo");
FindUsages find(src, doc, snapshot, true);
find(c);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Usage::Tag::Declaration);
QCOMPARE(find.usages()[0].line, 1);
QCOMPARE(find.usages()[0].col, 7);
QCOMPARE(find.usages()[1].tags, Usage::Tags());
QCOMPARE(find.usages()[1].line, 11);
QCOMPARE(find.usages()[1].col, 24);
}
{ // Test "templateFunc"
Class *c = doc->globalSymbolAt(1)->asClass();
QVERIFY(c);
QCOMPARE(c->name()->identifier()->chars(), "Bar");
QCOMPARE(c->memberCount(), 1);
Template *f = c->memberAt(0)->asTemplate();
QVERIFY(f);
QCOMPARE(f->name()->identifier()->chars(), "templateFunc");
FindUsages find(src, doc, snapshot, true);
find(f);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags,
(Usage::Tags{Usage::Tag::Declaration, Usage::Tag::Template}));
QCOMPARE(find.usages()[0].line, 4);
QCOMPARE(find.usages()[0].col, 7);
QCOMPARE(find.usages()[1].tags, Usage::Tags());
QCOMPARE(find.usages()[1].line, 11);
QCOMPARE(find.usages()[1].col, 11);
}
}
void tst_FindUsages::variadicFunctionTemplate()
{
const QByteArray src = "struct S{int value;};\n"
"template<class ... Types> S foo(Types & ... args){return S();}\n"
"int main(){\n"
" foo().value;\n"
" foo(1).value;\n"
" foo(1,2).value;\n"
"}";
Document::Ptr doc = Document::create("variadicFunctionTemplate");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount()>=1);
Snapshot snapshot;
snapshot.insert(doc);
{ // Test "S::value"
Class *c = doc->globalSymbolAt(0)->asClass();
QVERIFY(c);
QCOMPARE(c->name()->identifier()->chars(), "S");
QCOMPARE(c->memberCount(), 1);
Declaration *v = c->memberAt(0)->asDeclaration();
QVERIFY(v);
QCOMPARE(v->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(v);
QCOMPARE(find.usages().size(), 4);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(2).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(3).tags, Usage::Tag::Read);
}
}
void tst_FindUsages::typeTemplateParameterWithDefault()
{
const QByteArray src = "struct X{int value;};\n"
"struct S{int value;};\n"
"template<class T = S> T foo(){return T();}\n"
"int main(){\n"
" foo<X>().value;\n"
" foo<S>().value;\n"
" foo().value;\n" // this is S.value
"}";
Document::Ptr doc = Document::create("typeTemplateParameterWithDefault");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount()>=2);
Snapshot snapshot;
snapshot.insert(doc);
{ // Test "S::value"
Class *x = doc->globalSymbolAt(0)->asClass();
QVERIFY(x);
QCOMPARE(x->name()->identifier()->chars(), "X");
QCOMPARE(x->memberCount(), 1);
Class *s = doc->globalSymbolAt(1)->asClass();
QVERIFY(s);
QCOMPARE(s->name()->identifier()->chars(), "S");
QCOMPARE(s->memberCount(), 1);
Declaration *xv = x->memberAt(0)->asDeclaration();
QVERIFY(xv);
QCOMPARE(xv->name()->identifier()->chars(), "value");
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(xv);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
find(sv);
QCOMPARE(find.usages().size(), 3);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(2).tags, Usage::Tag::Read);
}
}
void tst_FindUsages::resolveOrder_for_templateFunction_vs_function()
{
const QByteArray src = "struct X{int value;};\n"
"struct S{int value;};\n"
"X foo(){return X();}\n"
"template<class T = S> T foo(){return T();}\n"
"int main(){\n"
" foo().value;\n" // this is X.value
"}";
Document::Ptr doc = Document::create("resolveOrder_for_templateFunction_vs_function");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount()>=1);
Snapshot snapshot;
snapshot.insert(doc);
{ // Test "S::value"
Class *x = doc->globalSymbolAt(0)->asClass();
QVERIFY(x);
QCOMPARE(x->name()->identifier()->chars(), "X");
QCOMPARE(x->memberCount(), 1);
Declaration *xv = x->memberAt(0)->asDeclaration();
QVERIFY(xv);
QCOMPARE(xv->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(xv);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
}
}
void tst_FindUsages::templateArrowOperator_with_defaultType()
{
const QByteArray src = "struct S{int value;};\n"
"struct C{\n"
" S* s;\n"
" template<class T = S> \n"
" T* operator->(){return &s;}\n"
"};\n"
"int main(){\n"
" C().operator -> ()->value;\n"
" C()->value;\n"
"}\n";
Document::Ptr doc = Document::create("templateArrowOperator_with_defaultType");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount()>=1);
Snapshot snapshot;
snapshot.insert(doc);
{ // Test "S::value"
Class *s = doc->globalSymbolAt(0)->asClass();
QVERIFY(s);
QCOMPARE(s->name()->identifier()->chars(), "S");
QCOMPARE(s->memberCount(), 1);
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(sv);
QCOMPARE(find.usages().size(), 3);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(2).tags, Usage::Tag::Read);
}
}
void tst_FindUsages::templateSpecialization_with_IntArgument()
{
const QByteArray src = "\n"
"struct S0{ int value = 0; };\n"
"struct S1{ int value = 1; };\n"
"struct S2{ int value = 2; };\n"
"template<int N> struct S { S0 s; };\n"
"template<> struct S<1> { S1 s; };\n"
"template<> struct S<2> { S2 s; };\n"
"int main()\n"
"{\n"
" S<0> s0;\n"
" S<1> s1;\n"
" S<2> s2;\n"
" s0.s.value;\n"
" s1.s.value;\n"
" s2.s.value = s2.s.value;\n"
"}\n";
Document::Ptr doc = Document::create("templateSpecialization_with_IntArgument");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount()>=3);
Snapshot snapshot;
snapshot.insert(doc);
{
Class *s[3] = {
doc->globalSymbolAt(0)->asClass(),
doc->globalSymbolAt(1)->asClass(),
doc->globalSymbolAt(2)->asClass(),
};
QVERIFY(s[0]);
QVERIFY(s[1]);
QVERIFY(s[2]);
QCOMPARE(s[0]->name()->identifier()->chars(), "S0");
QCOMPARE(s[1]->name()->identifier()->chars(), "S1");
QCOMPARE(s[2]->name()->identifier()->chars(), "S2");
QCOMPARE(s[0]->memberCount(), 1);
QCOMPARE(s[1]->memberCount(), 1);
QCOMPARE(s[2]->memberCount(), 1);
Declaration *sv[3] = {
s[0]->memberAt(0)->asDeclaration(),
s[1]->memberAt(0)->asDeclaration(),
s[2]->memberAt(0)->asDeclaration(),
};
QVERIFY(sv[0]);
QVERIFY(sv[1]);
QVERIFY(sv[2]);
QCOMPARE(sv[0]->name()->identifier()->chars(), "value");
QCOMPARE(sv[1]->name()->identifier()->chars(), "value");
QCOMPARE(sv[2]->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(sv[0]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].line, 1);
QCOMPARE(find.usages()[0].col, 15);
QCOMPARE(find.usages()[1].line, 12);
QCOMPARE(find.usages()[1].col, 9);
find(sv[1]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Initialization);
QCOMPARE(find.usages()[0].line, 2);
QCOMPARE(find.usages()[0].col, 15);
QCOMPARE(find.usages()[1].tags, Usage::Tag::Read);
QCOMPARE(find.usages()[1].line, 13);
QCOMPARE(find.usages()[1].col, 9);
find(sv[2]);
QCOMPARE(find.usages().size(), 3);
QCOMPARE(find.usages()[0].tags, Initialization);
QCOMPARE(find.usages()[0].line, 3);
QCOMPARE(find.usages()[0].col, 15);
QCOMPARE(find.usages()[1].tags, Usage::Tag::Write);
QCOMPARE(find.usages()[1].line, 14);
QCOMPARE(find.usages()[1].col, 9);
QCOMPARE(find.usages()[2].tags, Usage::Tag::Read);
QCOMPARE(find.usages()[2].line, 14);
QCOMPARE(find.usages()[2].col, 22);
}
}
void tst_FindUsages::templateSpecialization_with_BoolArgument()
{
const QByteArray src = "\n"
"struct S0{ int value = 0; };\n"
"struct S1{ int value = 1; };\n"
"template<bool B> struct S { S0 s; };\n"
"template<> struct S<true> { S1 s; };\n"
"int main()\n"
"{\n"
" S<false> s0;\n"
" S<true> s1;\n"
" s0.s.value;\n"
" s1.s.value;\n"
"}\n";
Document::Ptr doc = Document::create("templateSpecialization_with_BoolArgument");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount()>=3);
Snapshot snapshot;
snapshot.insert(doc);
{
Class *s[2] = {
doc->globalSymbolAt(0)->asClass(),
doc->globalSymbolAt(1)->asClass(),
};
QVERIFY(s[0]);
QVERIFY(s[1]);
QCOMPARE(s[0]->name()->identifier()->chars(), "S0");
QCOMPARE(s[1]->name()->identifier()->chars(), "S1");
QCOMPARE(s[0]->memberCount(), 1);
QCOMPARE(s[1]->memberCount(), 1);
Declaration *sv[2] = {
s[0]->memberAt(0)->asDeclaration(),
s[1]->memberAt(0)->asDeclaration(),
};
QVERIFY(sv[0]);
QVERIFY(sv[1]);
QCOMPARE(sv[0]->name()->identifier()->chars(), "value");
QCOMPARE(sv[1]->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(sv[0]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Initialization);
QCOMPARE(find.usages()[0].line, 1);
QCOMPARE(find.usages()[0].col, 15);
QCOMPARE(find.usages()[1].tags, Usage::Tag::Read);
QCOMPARE(find.usages()[1].line, 9);
QCOMPARE(find.usages()[1].col, 9);
find(sv[1]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Initialization);
QCOMPARE(find.usages()[0].line, 2);
QCOMPARE(find.usages()[0].col, 15);
QCOMPARE(find.usages()[1].tags, Usage::Tag::Read);
QCOMPARE(find.usages()[1].line, 10);
QCOMPARE(find.usages()[1].col, 9);
}
}
void tst_FindUsages::templatePartialSpecialization()
{
const QByteArray src = "\n"
"struct S0{ int value = 0; };\n"
"struct S1{ int value = 1; };\n"
"template<class T, class U> struct S { S0 ss; };\n"
"template<class U> struct S<float, U> { S1 ss; };\n"
"int main()\n"
"{\n"
" S<int, int> s0;\n"
" S<float, int> s1;\n"
" s0.ss.value;\n"
" s1.ss.value;\n"
"}\n";
Document::Ptr doc = Document::create("templatePartialSpecialization");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount()>=3);
Snapshot snapshot;
snapshot.insert(doc);
{
Class *s[2] = {
doc->globalSymbolAt(0)->asClass(),
doc->globalSymbolAt(1)->asClass(),
};
QVERIFY(s[0]);
QVERIFY(s[1]);
QCOMPARE(s[0]->name()->identifier()->chars(), "S0");
QCOMPARE(s[1]->name()->identifier()->chars(), "S1");
QCOMPARE(s[0]->memberCount(), 1);
QCOMPARE(s[1]->memberCount(), 1);
Declaration *sv[2] = {
s[0]->memberAt(0)->asDeclaration(),
s[1]->memberAt(0)->asDeclaration(),
};
QVERIFY(sv[0]);
QVERIFY(sv[1]);
QCOMPARE(sv[0]->name()->identifier()->chars(), "value");
QCOMPARE(sv[1]->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(sv[0]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Initialization);
QCOMPARE(find.usages()[0].line, 1);
QCOMPARE(find.usages()[0].col, 15);
QCOMPARE(find.usages()[1].tags, Usage::Tag::Read);
QCOMPARE(find.usages()[1].line, 9);
QCOMPARE(find.usages()[1].col, 10);
find(sv[1]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages()[0].tags, Initialization);
QCOMPARE(find.usages()[0].line, 2);
QCOMPARE(find.usages()[0].col, 15);
QCOMPARE(find.usages()[1].tags, Usage::Tag::Read);
QCOMPARE(find.usages()[1].line, 10);
QCOMPARE(find.usages()[1].col, 10);
}
}
void tst_FindUsages::templatePartialSpecialization_2()
{
const QByteArray src =
R"(
struct S0{int value=0;};
struct S1{int value=1;};
struct S2{int value=2;};
template<class T1, class T2> struct S{T1 ss;};
template<class U> struct S<int, U>{ U ss; };
template<class V> struct S<V*, int>{ V *ss; };
int main()
{
S<S0, float> s0;
s0.ss.value;
S<int, S1> s1;
s1.ss.value;
S<S2*, int> s2;
s2.ss->value;
}
)";
Document::Ptr doc = Document::create("templatePartialSpecialization_2");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount()>=3);
Snapshot snapshot;
snapshot.insert(doc);
FindUsages find(src, doc, snapshot, true);
Class *s[3];
Declaration *sv[3];
for (int i = 0; i < 3; i++) {
s[i] = doc->globalSymbolAt(i)->asClass();
QVERIFY(s[i]);
QCOMPARE(s[i]->memberCount(), 1);
sv[i] = s[i]->memberAt(0)->asDeclaration();
QVERIFY(sv[i]);
QCOMPARE(sv[i]->name()->identifier()->chars(), "value");
}
QCOMPARE(s[0]->name()->identifier()->chars(), "S0");
QCOMPARE(s[1]->name()->identifier()->chars(), "S1");
QCOMPARE(s[2]->name()->identifier()->chars(), "S2");
find(sv[0]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Initialization);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
find(sv[1]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Initialization);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
find(sv[2]);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Initialization);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::template_SFINAE_1()
{
const QByteArray src =
R"(
struct S{int value=1;};
template<class, class> struct is_same {};
template<class T> struct is_same<T, T> {using type = int;};
template<class T = S, typename is_same<T, S>::type = 0> T* foo(){return new T();}
int main(){
foo()->value;
}
)";
Document::Ptr doc = Document::create("template_SFINAE_1");
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(), "S");
QCOMPARE(s->memberCount(), 1);
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(sv);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Initialization);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::variableTemplateInExpression()
{
const QByteArray src =
R"(
struct S{int value;};
template<class T> constexpr int foo = sizeof(T);
template<class T1, class T2>
struct pair{
pair() noexcept(foo<T1> + foo<T2>){}
T1 first;
T2 second;
};
int main(){
pair<int, S> pair;
pair.second.value;
}
)";
Document::Ptr doc = Document::create("variableTemplateInExpression");
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(), "S");
QCOMPARE(s->memberCount(), 1);
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(sv);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::variadicMacros()
{
const QByteArray src =
R"(
struct MyStruct { int value; };
#define FOO( ... ) int foo()
FOO(1) {
MyStruct s;
s.value;
}
int main(){}
)";
Document::Ptr doc = Document::create("variadicMacros");
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(), "MyStruct");
QCOMPARE(s->memberCount(), 1);
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
FindUsages find(src, doc, snapshot, true);
find(sv);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Read);
}
void tst_FindUsages::writableRefs()
{
const QByteArray src = R"(
struct S {
S() : value2(value) {}
static int value;
int value2 : 2;
static void *p;
static const void *p2;
struct Nested {
int constFunc() const;
void constFunc(int) const;
void nonConstFunc();
} n;
Nested constFunc() const;
void nonConstFunc();
static void staticFunc1() {}
static void staticFunc2();
virtual void pureVirtual() = 0;
virtual void pureVirtual2() = 0 {}
};
void func1(int &);
void func2(const int &);
void func3(int *);
void func4(const int *);
void func5(int);
int main()
{
S s;
auto *p = &s.value;
int **pp;
p = &s.value;
*pp = &s.value;
//p = &s::value; FIXME: This one is not found at all...
s.p = &s.value;
// s::p = &s::value; FIXME: Same here.
(&s)->p = &((new S)->value);
const int *p2 = &s.value;
s.p2 = &s.value;
int * const p3 = &s.value;
int &r = s.value;
const int &cr = s.value;
func1(s.value);
func2(s.value);
func3(&s.value);
func4(&s.value);
func5(s.value);
*p = 5;
func1(*p);
func2(*p);
func3(p);
func4(p);
func5(p);
int &r2 = *p;
const int &cr2 = *p;
s = S();
auto * const ps = &s;
const auto *ps2 = &s;
auto &pr = s;
const auto pr2 = &s;
s.constFunc().nonConstFunc();
s.nonConstFunc();
(&s)->nonConstFunc();
s.n.constFunc();
s.n.nonConstFunc();
s.n.constFunc(s.value);
delete s.p;
switch (S::value) {
case S::value: break;
}
switch (S::value = 5) {
default: break;
}
if (S::value) {}
if (S::value = 0) {}
++S::value;
S::value--;
s.staticFunc1();
s.staticFunc2();
S::value = sizeof S::value;
int array[3];
array[S::value] = S::value;
S::value = array[S::value];
}
)";
const Document::Ptr doc = Document::create("writableRefs");
doc->setUtf8Source(src);
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QCOMPARE(doc->globalSymbolCount(), 7);
Snapshot snapshot;
snapshot.insert(doc);
Class * const structS = doc->globalSymbolAt(0)->asClass();
QVERIFY(structS);
QCOMPARE(structS->name()->identifier()->chars(), "S");
QCOMPARE(structS->memberCount(), 13);
Declaration * const sv = structS->memberAt(1)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
// Access to struct member
FindUsages find(src, doc, snapshot, true);
find(sv);
QCOMPARE(find.usages().size(), 31);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(2).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(3).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(4).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(5).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(6).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(7).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(8).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(9).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(10).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(11).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(12).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(13).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(14).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(15).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(16).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(17).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(18).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(19).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(20).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(21).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(22).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(23).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(24).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(25).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(26).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(27).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(28).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(29).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(30).tags, Usage::Tag::Read);
Declaration * const sv2 = structS->memberAt(2)->asDeclaration();
QVERIFY(sv2);
QCOMPARE(sv2->name()->identifier()->chars(), "value2");
// Member initialization in constructor
find(sv2);
QCOMPARE(find.usages().size(), 2);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Declaration);
// Make sure that pure virtual declaration is not mistaken for an assignment.
Declaration * const pureVirtual = structS->memberAt(11)->asDeclaration();
QVERIFY(pureVirtual);
QCOMPARE(pureVirtual->name()->identifier()->chars(), "pureVirtual");
find(pureVirtual);
QCOMPARE(find.usages().size(), 1);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
Function * const pureVirtual2 = structS->memberAt(12)->asFunction();
QVERIFY(pureVirtual2);
QCOMPARE(pureVirtual2->name()->identifier()->chars(), "pureVirtual2");
find(pureVirtual2);
QCOMPARE(find.usages().size(), 1);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
Function * const main = doc->globalSymbolAt(6)->asFunction();
QVERIFY(main);
QCOMPARE(main->memberCount(), 1);
Block * const block = main->memberAt(0)->asBlock();
QVERIFY(block);
QCOMPARE(block->memberCount(), 18);
// Access to pointer
Declaration * const p = block->memberAt(1)->asDeclaration();
QVERIFY(p);
QCOMPARE(p->name()->identifier()->chars(), "p");
find(p);
QCOMPARE(find.usages().size(), 10);
QCOMPARE(find.usages().at(0).tags, Initialization);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(2).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(3).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(4).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(5).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(6).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(7).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(8).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(9).tags, Usage::Tag::Read);
// Access to struct variable via its members
Declaration * const varS = block->memberAt(0)->asDeclaration();
QVERIFY(varS);
QCOMPARE(varS->name()->identifier()->chars(), "s");
find(varS);
QCOMPARE(find.usages().size(), 33);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(2).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(3).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(4).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(5).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(6).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(7).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(8).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(9).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(10).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(11).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(12).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(13).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(14).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(15).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(16).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(17).tags, Usage::Tag::Read);
// Direct access to struct variable
QCOMPARE(find.usages().at(18).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(19).tags, Usage::Tag::WritableRef);
QEXPECT_FAIL(nullptr, "parser does not record const qualifier for auto types", Continue);
QCOMPARE(find.usages().at(20).tags, Usage::Tag::Read);
QEXPECT_FAIL(nullptr, "parser does not record reference qualifier for auto types", Continue);
QCOMPARE(find.usages().at(21).tags, Usage::Tag::WritableRef);
QEXPECT_FAIL(nullptr, "parser does not record const qualifier for auto types", Continue);
QCOMPARE(find.usages().at(22).tags, Usage::Tag::Read);
// Member function calls.
QCOMPARE(find.usages().at(23).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(24).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(25).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(26).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(27).tags, Usage::Tag::WritableRef);
QCOMPARE(find.usages().at(28).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(29).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(31).tags, Usage::Tags());
QCOMPARE(find.usages().at(32).tags, Usage::Tags());
// Usages of struct type
find(structS);
QCOMPARE(find.usages().size(), 18);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::ConstructorDestructor);
QCOMPARE(find.usages().at(2).tags, Usage::Tags());
QCOMPARE(find.usages().at(3).tags, Usage::Tags());
QCOMPARE(find.usages().at(4).tags, Usage::Tags());
// These are conceptually questionable, as S is a type and thus we cannot "read from"
// or "write to" it. But it possibly matches the intuitive user expectation.
QCOMPARE(find.usages().at(5).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(6).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(7).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(8).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(9).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(10).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(11).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(12).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(13).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(14).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(15).tags, Usage::Tag::Read);
QCOMPARE(find.usages().at(16).tags, Usage::Tag::Write);
QCOMPARE(find.usages().at(17).tags, Usage::Tag::Read);
// Arrays.
Declaration * const array = block->memberAt(17)->asDeclaration();
QVERIFY(p);
QCOMPARE(array->name()->identifier()->chars(), "array");
find(array);
QCOMPARE(find.usages().size(), 3);
QCOMPARE(find.usages().at(0).tags, Usage::Tag::Declaration);
QCOMPARE(find.usages().at(1).tags, Usage::Tag::Write);
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"