// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include //TESTED_COMPONENT=src/libs/cplusplus using namespace CPlusPlus; class CollectNames: public ASTVisitor { public: CollectNames(TranslationUnit *xUnit): ASTVisitor(xUnit) {} QList 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 _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 &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 &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 struct Tmp { T str; }; template struct Tmp2 { Tmp2(){} T str; }; template struct Tmp3 { Tmp3(int i){} T str; }; int main() { auto tmp = Tmp(); auto tmp2 = Tmp2(); auto tmp3 = Tmp3(1); tmp.str.value; tmp2.str.value; tmp3.str.value; Tmp().str.value; Tmp2().str.value; Tmp3(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 struct Tmp { T str; MyStruct operator()() { return MyStruct(); } }; struct Simple { MyStruct str; MyStruct operator()() { return MyStruct(); } }; int main() { Tmp().str.value; Tmp()().value; Tmp 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(); QListexprs = 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 \n" "struct Template\n" "{\n" " struct Nested\n" " {\n" " T t;\n" " }nested;\n" "};\n" "void f()\n" "{\n" " Template 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\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::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\n" "struct Outer\n" "{\n" " struct Nested\n" " {\n" " T *operator->() { return 0; }\n" " };\n" "};\n" "\n" "void bug()\n" "{\n" " Outer::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 \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 \n" "struct TS\n" "{\n" " TS();\n" " ~TS();\n" "};\n" "template \n" "TS::TS()\n" "{\n" "}\n" "template \n" "TS::~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\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 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 struct Ptr { T* operator->() { return {}; } }; template Ptr makePtr() { return {}; } } int func() { auto s = N::makePtr(); 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 struct shared_ptr{};\n" "}\n" "namespace NS\n" "{\n" " using std::shared_ptr;\n" "}\n" "void fun()\n" "{\n" " NS::shared_ptr 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 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 \n" " T *templateFunc() { return 0; }\n" "};\n" "struct Test {\n" " Bar member;\n" " void testFunc();\n" "};\n" "void Test::testFunc() {\n" " member.templateFunc();\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 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 T foo(){return T();}\n" "int main(){\n" " foo().value;\n" " foo().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 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 \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 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 struct S { S0 s; };\n" "template<> struct S { S1 s; };\n" "int main()\n" "{\n" " S s0;\n" " S 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 struct S { S0 ss; };\n" "template struct S { S1 ss; };\n" "int main()\n" "{\n" " S s0;\n" " S 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 struct S{T1 ss;}; template struct S{ U ss; }; template struct S{ V *ss; }; int main() { S s0; s0.ss.value; S s1; s1.ss.value; S 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 struct is_same {}; template struct is_same {using type = int;}; template::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 constexpr int foo = sizeof(T); template struct pair{ pair() noexcept(foo + foo){} T1 first; T2 second; }; int main(){ pair 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"