File indexing completed on 2024-05-12 04:39:18

0001 /*
0002     SPDX-FileCopyrightText: 2014 Milian Wolff <mail@milianw.de>
0003     SPDX-FileCopyrightText: 2014 Kevin Funk <kfunk@kde.org>
0004     SPDX-FileCopyrightText: 2015 Sergey Kalinichev <kalinichev.so.0@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "test_duchain.h"
0010 
0011 #include <tests/testcore.h>
0012 #include <tests/autotestshell.h>
0013 #include <tests/testfile.h>
0014 #include <tests/testproject.h>
0015 #include <language/duchain/duchainlock.h>
0016 #include <language/duchain/duchain.h>
0017 #include <language/duchain/declaration.h>
0018 #include <language/duchain/parsingenvironment.h>
0019 #include <language/duchain/problem.h>
0020 #include <language/duchain/types/integraltype.h>
0021 #include <language/duchain/types/structuretype.h>
0022 #include <language/duchain/types/functiontype.h>
0023 #include <language/duchain/types/typealiastype.h>
0024 #include <language/duchain/types/typeutils.h>
0025 #include <language/duchain/duchainutils.h>
0026 #include <language/duchain/classdeclaration.h>
0027 #include <language/duchain/abstractfunctiondeclaration.h>
0028 #include <language/duchain/functiondefinition.h>
0029 #include <language/duchain/classfunctiondeclaration.h>
0030 #include <language/duchain/forwarddeclaration.h>
0031 #include <language/duchain/use.h>
0032 #include <language/duchain/duchaindumper.h>
0033 #include <language/backgroundparser/backgroundparser.h>
0034 #include <interfaces/ilanguagecontroller.h>
0035 #include <interfaces/idocumentcontroller.h>
0036 #include <util/kdevstringhandler.h>
0037 
0038 #include "duchain/clangparsingenvironmentfile.h"
0039 #include "duchain/clangparsingenvironment.h"
0040 #include "duchain/parsesession.h"
0041 #include "duchain/clanghelpers.h"
0042 #include "duchain/macrodefinition.h"
0043 
0044 #include "testprovider.h"
0045 
0046 #include <KConfigGroup>
0047 
0048 #include <QTest>
0049 #include <QSignalSpy>
0050 #include <QLoggingCategory>
0051 #include <QThread>
0052 #include <QVector>
0053 #include <QVersionNumber>
0054 
0055 QTEST_MAIN(TestDUChain)
0056 
0057 using namespace KDevelop;
0058 
0059 namespace KDevelop {
0060 char* toString(ClassMemberDeclaration::BitWidthSpecialValue bitWidth)
0061 {
0062     return QTest::toString(QString::number(bitWidth));
0063 }
0064 }
0065 
0066 TestDUChain::~TestDUChain() = default;
0067 
0068 void TestDUChain::initTestCase()
0069 {
0070     QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevelop.plugins.clang.debug=true\n"));
0071     QVERIFY(qputenv("KDEV_CLANG_DISPLAY_DIAGS", "1"));
0072     AutoTestShell::init({QStringLiteral("kdevclangsupport")});
0073     auto core = TestCore::initialize();
0074     delete core->projectController();
0075     m_projectController = new TestProjectController(core);
0076     core->setProjectController(m_projectController);
0077 }
0078 
0079 void TestDUChain::cleanupTestCase()
0080 {
0081     TestCore::shutdown();
0082 }
0083 
0084 void TestDUChain::cleanup()
0085 {
0086     if (m_provider) {
0087         IDefinesAndIncludesManager::manager()->unregisterBackgroundProvider(m_provider.data());
0088     }
0089     // process delayed events
0090     QTest::qWait(0);
0091 }
0092 
0093 void TestDUChain::init()
0094 {
0095     m_provider.reset(new TestEnvironmentProvider);
0096     IDefinesAndIncludesManager::manager()->registerBackgroundProvider(m_provider.data());
0097 }
0098 
0099 struct ExpectedComment
0100 {
0101     QString identifier;
0102     QString comment;
0103 };
0104 Q_DECLARE_METATYPE(ExpectedComment)
0105 Q_DECLARE_METATYPE(AbstractType::WhichType)
0106 
0107 void TestDUChain::testComments()
0108 {
0109     QFETCH(QString, code);
0110     QFETCH(ExpectedComment, expectedComment);
0111 
0112     TestFile file(code, QStringLiteral("cpp"));
0113     QVERIFY(file.parseAndWait());
0114 
0115     DUChainReadLocker lock;
0116     auto top = file.topContext();
0117     QVERIFY(top);
0118     auto candidates = top->findDeclarations(QualifiedIdentifier(expectedComment.identifier));
0119     QVERIFY(!candidates.isEmpty());
0120     auto decl = candidates.first();
0121     QString comment = QString::fromLocal8Bit(decl->comment());
0122     const auto plainText = KDevelop::htmlToPlainText(comment, KDevelop::CompleteMode);
0123     // if comment is e.g. "<this is bar", htmlToPlainText(comment) would just return an empty string; avoid this
0124     if (!plainText.isEmpty()) {
0125         comment = plainText;
0126     }
0127     QCOMPARE(comment, expectedComment.comment);
0128 }
0129 
0130 void TestDUChain::testComments_data()
0131 {
0132     QTest::addColumn<QString>("code");
0133     QTest::addColumn<ExpectedComment>("expectedComment");
0134 
0135     // note: Clang only retrieves the comments when in doxygen-style format (i.e. '///', '/**', '///<')
0136     QTest::newRow("invalid1")
0137         << "//this is foo\nint foo;"
0138         << ExpectedComment{"foo", QString()};
0139     QTest::newRow("invalid2")
0140         << "/*this is foo*/\nint foo;"
0141         << ExpectedComment{"foo", QString()};
0142     QTest::newRow("basic1")
0143         << "///this is foo\nint foo;"
0144         << ExpectedComment{"foo", "this is foo"};
0145     QTest::newRow("basic2")
0146         << "/**this is foo*/\nint foo;"
0147         << ExpectedComment{"foo", "this is foo"};
0148 
0149     // as long as https://bugs.llvm.org/show_bug.cgi?id=35333 is not fixed, we don't fully parse and render
0150     // doxygen-style comments properly (cf. `makeComment` in builder.cpp)
0151 #define PARSE_COMMENTS 0
0152     QTest::newRow("enumerator")
0153         << "enum Foo { bar1, ///<this is bar1\nbar2 ///<this is bar2\n };"
0154         << ExpectedComment{"Foo::bar1", "this is bar1"};
0155 
0156     QTest::newRow("comment-formatting")
0157         << "/** a\n * multiline\n *\n * comment\n */ int foo;"
0158 #if PARSE_COMMENTS
0159         << ExpectedComment{"foo", "a multiline\ncomment"};
0160 #else
0161         << ExpectedComment{"foo", "a multiline comment"};
0162 #endif
0163     QTest::newRow("comment-doxygen-tags")
0164         << "/** @see bar()\n@param a foo\n*/\nvoid foo(int a);\nvoid bar();"
0165 #if PARSE_COMMENTS
0166         << ExpectedComment{"foo", "bar()\na\nfoo"};
0167 #else
0168         << ExpectedComment{"foo", "@see bar() @param a foo"};
0169 #endif
0170 }
0171 
0172 void TestDUChain::testElaboratedType()
0173 {
0174     QFETCH(QString, code);
0175     QFETCH(AbstractType::WhichType, type);
0176 
0177     TestFile file(code, QStringLiteral("cpp"));
0178     QVERIFY(file.parseAndWait());
0179 
0180     DUChainReadLocker lock;
0181     auto top = file.topContext();
0182     QVERIFY(top);
0183     QCOMPARE(file.topContext()->localDeclarations().size(), 2);
0184 
0185     auto decl = file.topContext()->localDeclarations()[1];
0186     QVERIFY(decl);
0187 
0188     auto function = dynamic_cast<FunctionDeclaration*>(decl);
0189     QVERIFY(function);
0190 
0191     auto functionType = function->type<FunctionType>();
0192     QVERIFY(functionType);
0193 
0194 #if CINDEX_VERSION_MINOR < 34
0195     QEXPECT_FAIL("namespace", "The ElaboratedType is not exposed through the libclang interface, not much we can do here", Abort);
0196 #endif
0197     QVERIFY(functionType->returnType()->whichType() != AbstractType::TypeDelayed);
0198 #if CINDEX_VERSION_MINOR < 34
0199     QEXPECT_FAIL("typedef", "After using clang_getCanonicalType on ElaboratedType all typedef information gets stripped away", Continue);
0200 #endif
0201     QCOMPARE(functionType->returnType()->whichType(), type);
0202 }
0203 
0204 void TestDUChain::testElaboratedType_data()
0205 {
0206     QTest::addColumn<QString>("code");
0207     QTest::addColumn<AbstractType::WhichType>("type");
0208 
0209     QTest::newRow("namespace")
0210         << "namespace NS{struct Type{};} struct NS::Type foo();"
0211         << AbstractType::TypeStructure;
0212     QTest::newRow("enum")
0213         << "enum Enum{}; enum Enum foo();"
0214         << AbstractType::TypeEnumeration;
0215     QTest::newRow("typedef")
0216         << "namespace NS{typedef int type;} NS::type foo();"
0217         << AbstractType::TypeAlias;
0218 }
0219 
0220 void TestDUChain::testMacroDefinition()
0221 {
0222     QFETCH(QString, code);
0223     QFETCH(QString, definition);
0224     QFETCH(bool, isFunctionLike);
0225     QFETCH(QVector<QString>, parameters);
0226 
0227     TestFile file(code, QStringLiteral("cpp"));
0228     QVERIFY(file.parseAndWait());
0229 
0230     DUChainReadLocker lock;
0231     auto top = file.topContext();
0232     QVERIFY(top);
0233     QCOMPARE(top->localDeclarations().size(), 1);
0234 
0235     const auto decl = top->localDeclarations().constFirst();
0236     QVERIFY(decl);
0237 
0238     const auto macro = dynamic_cast<const MacroDefinition*>(decl);
0239     QVERIFY(macro);
0240 
0241     QCOMPARE(macro->definition().str(), definition);
0242 
0243     QCOMPARE(macro->isFunctionLike(), isFunctionLike);
0244 
0245     // The displayed tooltip for these incorrectly parsed macros looks good, but the bogus
0246     // single macro parameter in the MacroDefinition object could cause issues in the future.
0247     QEXPECT_FAIL("only_escaped_newline_in_parens(\\\n)", "difficult to parse, uncommon, not yet a problem", Continue);
0248     QEXPECT_FAIL("only_comment_in_parens(/*comment*/)", "difficult to parse, uncommon, not yet a problem", Continue);
0249     QCOMPARE(macro->parametersSize(), parameters.size());
0250 
0251     const IndexedString* actualParam = macro->parameters();
0252     for (const auto& expectedParam : parameters) {
0253         QCOMPARE(actualParam->str(), expectedParam);
0254         ++actualParam;
0255     }
0256 }
0257 
0258 void TestDUChain::testMacroDefinition_data()
0259 {
0260     QTest::addColumn<QString>("code");
0261     QTest::addColumn<QString>("definition");
0262     QTest::addColumn<bool>("isFunctionLike");
0263     QTest::addColumn<QVector<QString>>("parameters");
0264 
0265     const auto addTest = [](const QString& code, const QString& definition, bool isFunctionLike = false,
0266                             const QVector<QString>& parameters = {}) {
0267         QTest::addRow("%s", qPrintable(code))
0268             << QString{"#define " + code} << definition << isFunctionLike << parameters;
0269     };
0270 
0271     addTest("macro", "");
0272     addTest("m1 1", "1");
0273     addTest("m int x", "int x");
0274     addTest("m (u + v)", "(u + v)");
0275     addTest("m\tn", "n");
0276     addTest("m/*o*/long", "/*o*/long");
0277     addTest("m/*(x)*/ long", "/*(x)*/ long");
0278 
0279     addTest("m()", "", true);
0280     addTest("macro(    ) C", "C", true);
0281 
0282     addTest("mac(x)", "", true, {"x"});
0283     addTest("VARG_1(...)", "", true, {"..."});
0284     addTest("mac(x) x", "x", true, {"x"});
0285     addTest("mc(u)\tu *2 \t/  Limit", "u *2 \t/  Limit", true, {"u"});
0286     addTest("m(x)x", "x", true, {"x"});
0287     addTest("m(x)long x", "long x", true, {"x"});
0288     addTest("m(x)/*A*/x", "/*A*/x", true, {"x"});
0289     addTest("XYZ_(...) __VA_ARGS__", "__VA_ARGS__", true, {"..."});
0290 
0291     addTest("macro(may,be)", "", true, {"may", "be"});
0292     addTest("m(x, \ty)\tx", "x", true, {"x", "y"});
0293     addTest("m(x, y) x / y", "x / y", true, {"x", "y"});
0294     addTest("M_N(X, ...) f(X __VA_OPT__(,) __VA_ARGS__)", "f(X __VA_OPT__(,) __VA_ARGS__)", true, {"X", "..."});
0295 
0296     addTest("m( /*a)b*/ x )x", "x", true, {"/*a)b*/ x"});
0297     addTest("_(\t/* u,v,*/c\t)\tc/3*(c-5)", "c/3*(c-5)", true, {"/* u,v,*/c"});
0298 
0299     addTest("MM\t\\\n\t471", "\\\n\t471");
0300     addTest("S1\\\n get()", "get()");
0301     addTest("S1\\\nget(\t)", "", true);
0302     addTest("m_2\\  \t\n(){return;}", "{return;}", true);
0303     addTest("A0B9\\\n(N)\\\n(N-9)", "\\\n(N-9)", true, {"N"});
0304     addTest("\t     \\\t\nmacro\\ \n\\\nIden\\\ntifier(x,\\\t\n    y)x \t\ty", "x \t\ty", true, {"x", "\\\t\n    y"});
0305 
0306     addTest("only_escaped_newline_in_parens(\\\n)", "", true);
0307     addTest("only_comment_in_parens(/*comment*/)", "", true);
0308 }
0309 
0310 void TestDUChain::testInclude()
0311 {
0312     TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h"));
0313     // NOTE: header is _not_ explicitly being parsed, instead the impl job does that
0314 
0315     TestFile impl("#include \"" + header.url().str() + "\"\n"
0316                   "int main() { return foo(); }", QStringLiteral("cpp"), &header);
0317     impl.parse(TopDUContext::AllDeclarationsContextsAndUses);
0318 
0319     auto implCtx = impl.topContext();
0320     QVERIFY(implCtx);
0321 
0322     DUChainReadLocker lock;
0323     QCOMPARE(implCtx->localDeclarations().size(), 1);
0324 
0325     auto headerCtx = DUChain::self()->chainForDocument(header.url());
0326     QVERIFY(headerCtx);
0327     QVERIFY(!headerCtx->parsingEnvironmentFile()->needsUpdate());
0328     QCOMPARE(headerCtx->localDeclarations().size(), 1);
0329 
0330     QVERIFY(implCtx->imports(headerCtx, CursorInRevision(0, 10)));
0331 
0332     Declaration* foo = headerCtx->localDeclarations().first();
0333     QCOMPARE(foo->uses().size(), 1);
0334     QCOMPARE(foo->uses().begin().key(), impl.url());
0335     QCOMPARE(foo->uses().begin()->size(), 1);
0336     QCOMPARE(foo->uses().begin()->first(), RangeInRevision(1, 20, 1, 23));
0337 }
0338 
0339 void TestDUChain::testMissingInclude()
0340 {
0341     auto code = R"(
0342 #pragma once
0343 #include "missing1.h"
0344 
0345 template<class T>
0346 class A
0347 {
0348     T a;
0349 };
0350 
0351 #include "missing2.h"
0352 
0353 class B : public A<int>
0354 {
0355 };
0356     )";
0357 
0358     TestFile header(code, QStringLiteral("h"));
0359     TestFile impl("#include \"" + header.url().str() + "\"\n", QStringLiteral("cpp"), &header);
0360     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses));
0361 
0362     DUChainReadLocker lock;
0363 
0364     auto top = impl.topContext();
0365     QVERIFY(top);
0366 
0367     QCOMPARE(top->importedParentContexts().count(), 1);
0368 
0369     TopDUContext* headerCtx = dynamic_cast<TopDUContext*>(top->importedParentContexts().first().context(top));
0370     QVERIFY(headerCtx);
0371     QCOMPARE(headerCtx->url(), header.url());
0372 
0373 #if CINDEX_VERSION_MINOR < 34
0374     QEXPECT_FAIL("", "Second missing header isn't reported", Continue);
0375 #endif
0376     QCOMPARE(headerCtx->problems().count(), 2);
0377 
0378     QCOMPARE(headerCtx->localDeclarations().count(), 2);
0379 
0380     auto a = dynamic_cast<ClassDeclaration*>(headerCtx->localDeclarations().first());
0381     QVERIFY(a);
0382 
0383     auto b = dynamic_cast<ClassDeclaration*>(headerCtx->localDeclarations().last());
0384     QVERIFY(b);
0385 
0386     // NOTE: This fails and needs fixing. If the include of "missing2.h"
0387     //       above is commented out, then it doesn't fail. Maybe
0388     //       clang stops processing when it encounters the second missing
0389     //       header, or similar.
0390     // XFAIL this check until https://bugs.llvm.org/show_bug.cgi?id=38155 is fixed
0391     if (QVersionNumber::fromString(ClangHelpers::clangVersion()) < QVersionNumber(9, 0, 0))
0392         QEXPECT_FAIL("", "Base class isn't assigned correctly", Continue);
0393     QCOMPARE(b->baseClassesSize(), 1u);
0394 
0395 #if CINDEX_VERSION_MINOR < 34
0396     // at least the one problem we have should have been propagated
0397     QCOMPARE(top->problems().count(), 1);
0398 #else
0399     // two errors:
0400     // /tmp/testfile_f32415.h:3:10: error: 'missing1.h' file not found
0401     // /tmp/testfile_f32415.h:11:10: error: 'missing2.h' file not found
0402     QCOMPARE(top->problems().count(), 2);
0403 #endif
0404 }
0405 
0406 QByteArray createCode(const QByteArray& prefix, const int functions)
0407 {
0408     QByteArray code;
0409     code += "#ifndef " + prefix + "_H\n";
0410     code += "#define " + prefix + "_H\n";
0411     for (int i = 0; i < functions; ++i) {
0412         code += "void myFunc_" + prefix + "(int arg1, char arg2, const char* arg3);\n";
0413     }
0414     code += "#endif\n";
0415     return code;
0416 }
0417 
0418 void TestDUChain::testIncludeLocking()
0419 {
0420     TestFile header1(createCode("Header1", 1000), QStringLiteral("h"));
0421     TestFile header2(createCode("Header2", 1000), QStringLiteral("h"));
0422     TestFile header3(createCode("Header3", 1000), QStringLiteral("h"));
0423 
0424     ICore::self()->languageController()->backgroundParser()->setThreadCount(3);
0425 
0426     TestFile impl1("#include \"" + header1.url().str() + "\"\n"
0427                    "#include \"" + header2.url().str() + "\"\n"
0428                    "#include \"" + header3.url().str() + "\"\n"
0429                    "int main() { return 0; }", QStringLiteral("cpp"));
0430 
0431     TestFile impl2("#include \"" + header2.url().str() + "\"\n"
0432                    "#include \"" + header1.url().str() + "\"\n"
0433                    "#include \"" + header3.url().str() + "\"\n"
0434                    "int main() { return 0; }", QStringLiteral("cpp"));
0435 
0436     TestFile impl3("#include \"" + header3.url().str() + "\"\n"
0437                    "#include \"" + header1.url().str() + "\"\n"
0438                    "#include \"" + header2.url().str() + "\"\n"
0439                    "int main() { return 0; }", QStringLiteral("cpp"));
0440 
0441     impl1.parse(TopDUContext::AllDeclarationsContextsAndUses);
0442     impl2.parse(TopDUContext::AllDeclarationsContextsAndUses);
0443     impl3.parse(TopDUContext::AllDeclarationsContextsAndUses);
0444 
0445     QVERIFY(impl1.waitForParsed(5000));
0446     QVERIFY(impl2.waitForParsed(5000));
0447     QVERIFY(impl3.waitForParsed(5000));
0448 
0449     DUChainReadLocker lock;
0450     QVERIFY(DUChain::self()->chainForDocument(header1.url()));
0451     QVERIFY(DUChain::self()->chainForDocument(header2.url()));
0452     QVERIFY(DUChain::self()->chainForDocument(header3.url()));
0453 }
0454 
0455 void TestDUChain::testReparse()
0456 {
0457     TestFile file(QStringLiteral("int main() { int i = 42; return i; }"), QStringLiteral("cpp"));
0458     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
0459 
0460     DeclarationPointer mainDecl;
0461     DeclarationPointer iDecl;
0462     for (int i = 0; i < 3; ++i) {
0463         QVERIFY(file.waitForParsed(500));
0464         DUChainReadLocker lock;
0465         QVERIFY(file.topContext());
0466         QCOMPARE(file.topContext()->childContexts().size(), 1);
0467         QCOMPARE(file.topContext()->localDeclarations().size(), 1);
0468         DUContext *exprContext = file.topContext()->childContexts().first()->childContexts().first();
0469         QCOMPARE(exprContext->localDeclarations().size(), 1);
0470 
0471         if (i) {
0472             QVERIFY(mainDecl);
0473             QCOMPARE(mainDecl.data(), file.topContext()->localDeclarations().first());
0474 
0475             QVERIFY(iDecl);
0476             QCOMPARE(iDecl.data(), exprContext->localDeclarations().first());
0477         }
0478         mainDecl = file.topContext()->localDeclarations().first();
0479         iDecl = exprContext->localDeclarations().first();
0480 
0481         QVERIFY(mainDecl->uses().isEmpty());
0482         QCOMPARE(iDecl->uses().size(), 1);
0483         QCOMPARE(iDecl->uses().begin()->size(), 1);
0484 
0485         if (i == 1) {
0486             file.setFileContents(QStringLiteral("int main()\n{\nfloat i = 13; return i - 5;\n}\n"));
0487         }
0488 
0489         file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive);
0490     }
0491 }
0492 
0493 void TestDUChain::testReparseError()
0494 {
0495     TestFile file(QStringLiteral("int i = 1 / 0;\n"), QStringLiteral("cpp"));
0496     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
0497 
0498     for (int i = 0; i < 2; ++i) {
0499         QVERIFY(file.waitForParsed(500));
0500         DUChainReadLocker lock;
0501         QVERIFY(file.topContext());
0502         if (!i) {
0503             QCOMPARE(file.topContext()->problems().size(), 1);
0504             file.setFileContents(QStringLiteral("int i = 0;\n"));
0505         } else {
0506             QCOMPARE(file.topContext()->problems().size(), 0);
0507         }
0508 
0509         file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive);
0510     }
0511 }
0512 
0513 void TestDUChain::testTemplate()
0514 {
0515     TestFile file("template<typename T> struct foo { T bar; };\n"
0516                   "int main() { foo<int> myFoo; return myFoo.bar; }\n", QStringLiteral("cpp"));
0517     QVERIFY(file.parseAndWait());
0518 
0519     DUChainReadLocker lock;
0520     QVERIFY(file.topContext());
0521     QCOMPARE(file.topContext()->localDeclarations().size(), 2);
0522     auto fooDecl = file.topContext()->localDeclarations().first();
0523     QVERIFY(fooDecl->internalContext());
0524     QCOMPARE(fooDecl->internalContext()->localDeclarations().size(), 2);
0525 
0526     QCOMPARE(file.topContext()->findDeclarations(QualifiedIdentifier(u"foo< T >")).size(), 1);
0527     QCOMPARE(file.topContext()->findDeclarations(QualifiedIdentifier(u"foo< T >::bar")).size(), 1);
0528 
0529     auto mainCtx = file.topContext()->localDeclarations().last()->internalContext()->childContexts().first();
0530     QVERIFY(mainCtx);
0531     auto myFoo = mainCtx->localDeclarations().first();
0532     QVERIFY(myFoo);
0533     QCOMPARE(myFoo->abstractType()->toString().remove(' '), QStringLiteral("foo<int>"));
0534 }
0535 
0536 void TestDUChain::testNamespace()
0537 {
0538     TestFile file("namespace foo { struct bar { int baz; }; }\n"
0539                   "int main() { foo::bar myBar; }\n", QStringLiteral("cpp"));
0540     QVERIFY(file.parseAndWait());
0541 
0542     DUChainReadLocker lock;
0543     QVERIFY(file.topContext());
0544     QCOMPARE(file.topContext()->localDeclarations().size(), 2);
0545     auto fooDecl = file.topContext()->localDeclarations().first();
0546     QVERIFY(fooDecl->internalContext());
0547     QCOMPARE(fooDecl->internalContext()->localDeclarations().size(), 1);
0548 
0549     DUContext* top = file.topContext().data();
0550     DUContext* mainCtx = file.topContext()->childContexts().last();
0551 
0552     auto foo = top->localDeclarations().first();
0553     QCOMPARE(foo->qualifiedIdentifier().toString(), QString("foo"));
0554 
0555     DUContext* fooCtx = file.topContext()->childContexts().first();
0556     QCOMPARE(fooCtx->localScopeIdentifier().toString(), QString("foo"));
0557     QCOMPARE(fooCtx->scopeIdentifier(true).toString(), QString("foo"));
0558     QCOMPARE(fooCtx->localDeclarations().size(), 1);
0559     auto bar = fooCtx->localDeclarations().first();
0560     QCOMPARE(bar->qualifiedIdentifier().toString(), QString("foo::bar"));
0561     QCOMPARE(fooCtx->childContexts().size(), 1);
0562 
0563     DUContext* barCtx = fooCtx->childContexts().first();
0564     QCOMPARE(barCtx->localScopeIdentifier().toString(), QString("bar"));
0565     QCOMPARE(barCtx->scopeIdentifier(true).toString(), QString("foo::bar"));
0566     QCOMPARE(barCtx->localDeclarations().size(), 1);
0567     auto baz = barCtx->localDeclarations().first();
0568     QCOMPARE(baz->qualifiedIdentifier().toString(), QString("foo::bar::baz"));
0569 
0570     for (auto ctx : {top, mainCtx}) {
0571         QCOMPARE(ctx->findDeclarations(QualifiedIdentifier(u"foo")).size(), 1);
0572         QCOMPARE(ctx->findDeclarations(QualifiedIdentifier(u"foo::bar")).size(), 1);
0573         QCOMPARE(ctx->findDeclarations(QualifiedIdentifier(u"foo::bar::baz")).size(), 1);
0574     }
0575 }
0576 
0577 void TestDUChain::testAutoTypeDeduction()
0578 {
0579     TestFile file(QStringLiteral(R"(
0580         const volatile auto foo = 5;
0581         template<class T> struct myTemplate {};
0582         myTemplate<myTemplate<int>& > templRefParam;
0583         auto autoTemplRefParam = templRefParam;
0584     )"), QStringLiteral("cpp"));
0585     QVERIFY(file.parseAndWait());
0586 
0587     DUChainReadLocker lock;
0588 
0589     DUContext* ctx = file.topContext().data();
0590     QVERIFY(ctx);
0591     QCOMPARE(ctx->localDeclarations().size(), 4);
0592     QCOMPARE(ctx->findDeclarations(QualifiedIdentifier(u"foo")).size(), 1);
0593     Declaration* decl = ctx->findDeclarations(QualifiedIdentifier(QStringLiteral("foo")))[0];
0594     QCOMPARE(decl->identifier(), Identifier(u"foo"));
0595 #if CINDEX_VERSION_MINOR < 31
0596     QEXPECT_FAIL("", "No type deduction here unfortunately, missing API in Clang", Continue);
0597 #endif
0598     QVERIFY(decl->type<IntegralType>());
0599 #if CINDEX_VERSION_MINOR < 31
0600     QCOMPARE(decl->toString(), QStringLiteral("const volatile auto foo"));
0601 #else
0602     QCOMPARE(decl->toString(), QStringLiteral("const volatile int foo"));
0603 #endif
0604 
0605     decl = ctx->findDeclarations(QualifiedIdentifier(QStringLiteral("autoTemplRefParam")))[0];
0606     QVERIFY(decl);
0607     QVERIFY(decl->abstractType());
0608 #if CINDEX_VERSION_MINOR < 31
0609     QEXPECT_FAIL("", "Auto type is not exposed via LibClang", Continue);
0610 #endif
0611     QCOMPARE(decl->abstractType()->toString(), QStringLiteral("myTemplate< myTemplate< int >& >"));
0612 }
0613 
0614 void TestDUChain::testTypeDeductionInTemplateInstantiation()
0615 {
0616     // see: http://clang-developers.42468.n3.nabble.com/RFC-missing-libclang-query-functions-features-td2504253.html
0617     TestFile file(QStringLiteral("template<typename T> struct foo { T member; } foo<int> f; auto i = f.member;"), QStringLiteral("cpp"));
0618     QVERIFY(file.parseAndWait());
0619 
0620     DUChainReadLocker lock;
0621 
0622     DUContext* ctx = file.topContext().data();
0623     QVERIFY(ctx);
0624     QCOMPARE(ctx->localDeclarations().size(), 3);
0625     Declaration* decl = nullptr;
0626 
0627     // check 'foo' declaration
0628     decl = ctx->localDeclarations()[0];
0629     QVERIFY(decl);
0630     QCOMPARE(decl->identifier(), Identifier(u"foo< T >"));
0631 
0632     // check type of 'member' inside declaration-scope
0633     QCOMPARE(ctx->childContexts().size(), 1);
0634     DUContext* fooCtx = ctx->childContexts().first();
0635     QVERIFY(fooCtx);
0636     // Should there really be two declarations?
0637     QCOMPARE(fooCtx->localDeclarations().size(), 2);
0638     decl = fooCtx->localDeclarations()[1];
0639     QCOMPARE(decl->identifier(), Identifier(u"member"));
0640 
0641     // check type of 'member' in definition of 'f'
0642     decl = ctx->localDeclarations()[1];
0643     QCOMPARE(decl->identifier(), Identifier(u"f"));
0644     decl = ctx->localDeclarations()[2];
0645     QCOMPARE(decl->identifier(), Identifier(u"i"));
0646 #if CINDEX_VERSION_MINOR < 31
0647     QEXPECT_FAIL("", "No type deduction here unfortunately, missing API in Clang", Continue);
0648 #endif
0649     QVERIFY(decl->type<IntegralType>());
0650 }
0651 
0652 void TestDUChain::testVirtualMemberFunction()
0653 {
0654     //Forward-declarations with "struct" or "class" are considered equal, so make sure the override is detected correctly.
0655     TestFile file(QStringLiteral("struct S {}; struct A { virtual S* ret(); }; struct B : public A { virtual S* ret(); };"), QStringLiteral("cpp"));
0656     QVERIFY(file.parseAndWait());
0657 
0658     DUChainReadLocker lock;
0659     DUContext* top = file.topContext().data();
0660     QVERIFY(top);
0661 
0662     QCOMPARE(top->childContexts().count(), 3);
0663     QCOMPARE(top->localDeclarations().count(), 3);
0664     QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
0665     Declaration* decl = top->childContexts()[2]->localDeclarations()[0];
0666     QCOMPARE(decl->identifier(), Identifier(u"ret"));
0667     QVERIFY(DUChainUtils::overridden(decl));
0668 }
0669 
0670 void TestDUChain::testBaseClasses()
0671 {
0672     TestFile file(QStringLiteral("class Base {}; class Inherited : public Base {};"), QStringLiteral("cpp"));
0673     QVERIFY(file.parseAndWait());
0674 
0675     DUChainReadLocker lock;
0676     DUContext* top = file.topContext().data();
0677     QVERIFY(top);
0678 
0679     QCOMPARE(top->localDeclarations().count(), 2);
0680     Declaration* baseDecl = top->localDeclarations().first();
0681     QCOMPARE(baseDecl->identifier(), Identifier(u"Base"));
0682 
0683     ClassDeclaration* inheritedDecl = dynamic_cast<ClassDeclaration*>(top->localDeclarations()[1]);
0684     QCOMPARE(inheritedDecl->identifier(), Identifier(u"Inherited"));
0685 
0686     QVERIFY(inheritedDecl);
0687     QCOMPARE(inheritedDecl->baseClassesSize(), 1u);
0688 
0689     QCOMPARE(baseDecl->uses().count(), 1);
0690     QCOMPARE(baseDecl->uses().first().count(), 1);
0691     QCOMPARE(baseDecl->uses().first().first(), RangeInRevision(0, 40, 0, 44));
0692 }
0693 
0694 void TestDUChain::testReparseBaseClasses()
0695 {
0696     TestFile file(QStringLiteral("struct a{}; struct b : a {};\n"), QStringLiteral("cpp"));
0697     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
0698 
0699     for (int i = 0; i < 2; ++i) {
0700         qDebug() << "run: " << i;
0701         QVERIFY(file.waitForParsed(500));
0702         DUChainWriteLocker lock;
0703         QVERIFY(file.topContext());
0704         QCOMPARE(file.topContext()->childContexts().size(), 2);
0705         QCOMPARE(file.topContext()->childContexts().first()->importers().size(), 1);
0706         QCOMPARE(file.topContext()->childContexts().last()->importedParentContexts().size(), 1);
0707 
0708         QCOMPARE(file.topContext()->localDeclarations().size(), 2);
0709         auto aDecl = dynamic_cast<ClassDeclaration*>(file.topContext()->localDeclarations().first());
0710         QVERIFY(aDecl);
0711         QCOMPARE(aDecl->baseClassesSize(), 0u);
0712         auto bDecl = dynamic_cast<ClassDeclaration*>(file.topContext()->localDeclarations().last());
0713         QVERIFY(bDecl);
0714         QCOMPARE(bDecl->baseClassesSize(), 1u);
0715         int distance = 0;
0716         QVERIFY(bDecl->isPublicBaseClass(aDecl, file.topContext(), &distance));
0717         QCOMPARE(distance, 1);
0718 
0719         file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive);
0720     }
0721 }
0722 
0723 void TestDUChain::testReparseBaseClassesTemplates()
0724 {
0725     TestFile file(QStringLiteral("template<typename T> struct a{}; struct b : a<int> {};\n"), QStringLiteral("cpp"));
0726     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
0727 
0728     for (int i = 0; i < 2; ++i) {
0729         qDebug() << "run: " << i;
0730         QVERIFY(file.waitForParsed(500));
0731         DUChainWriteLocker lock;
0732         QVERIFY(file.topContext());
0733         QCOMPARE(file.topContext()->childContexts().size(), 2);
0734         QCOMPARE(file.topContext()->childContexts().first()->importers().size(), 1);
0735         QCOMPARE(file.topContext()->childContexts().last()->importedParentContexts().size(), 1);
0736 
0737         QCOMPARE(file.topContext()->localDeclarations().size(), 2);
0738         auto aDecl = dynamic_cast<ClassDeclaration*>(file.topContext()->localDeclarations().first());
0739         QVERIFY(aDecl);
0740         QCOMPARE(aDecl->baseClassesSize(), 0u);
0741         auto bDecl = dynamic_cast<ClassDeclaration*>(file.topContext()->localDeclarations().last());
0742         QVERIFY(bDecl);
0743         QCOMPARE(bDecl->baseClassesSize(), 1u);
0744         int distance = 0;
0745         QVERIFY(bDecl->isPublicBaseClass(aDecl, file.topContext(), &distance));
0746         QCOMPARE(distance, 1);
0747 
0748         file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive);
0749     }
0750 }
0751 
0752 void TestDUChain::testGetInheriters_data()
0753 {
0754     QTest::addColumn<QString>("code");
0755 
0756     QTest::newRow("inline") << "struct Base { struct Inner {}; }; struct Inherited : Base, Base::Inner {};";
0757     QTest::newRow("outline") << "struct Base { struct Inner; }; struct Base::Inner {}; struct Inherited : Base, Base::Inner {};";
0758 }
0759 
0760 void TestDUChain::testGetInheriters()
0761 {
0762     QFETCH(QString, code);
0763     TestFile file(code, QStringLiteral("cpp"));
0764     QVERIFY(file.parseAndWait());
0765 
0766     DUChainReadLocker lock;
0767     auto top = file.topContext();
0768     QVERIFY(top);
0769     QVERIFY(top->problems().isEmpty());
0770 
0771     QCOMPARE(top->localDeclarations().count(), 2);
0772     Declaration* baseDecl = top->localDeclarations().first();
0773     QCOMPARE(baseDecl->identifier(), Identifier(u"Base"));
0774 
0775     DUContext* baseCtx = baseDecl->internalContext();
0776     QVERIFY(baseCtx);
0777     QCOMPARE(baseCtx->localDeclarations().count(), 1);
0778 
0779     Declaration* innerDecl = baseCtx->localDeclarations().first();
0780     QCOMPARE(innerDecl->identifier(), Identifier(u"Inner"));
0781     if (auto forward = dynamic_cast<ForwardDeclaration*>(innerDecl)) {
0782         innerDecl = forward->resolve(top);
0783     }
0784     QVERIFY(dynamic_cast<ClassDeclaration*>(innerDecl));
0785 
0786     Declaration* inheritedDecl = top->localDeclarations().last();
0787     QVERIFY(inheritedDecl);
0788     QCOMPARE(inheritedDecl->identifier(), Identifier(u"Inherited"));
0789 
0790     uint maxAllowedSteps = uint(-1);
0791     auto baseInheriters = DUChainUtils::inheriters(baseDecl, maxAllowedSteps);
0792     QCOMPARE(baseInheriters, QList<Declaration*>() << inheritedDecl);
0793 
0794     maxAllowedSteps = uint(-1);
0795     auto innerInheriters = DUChainUtils::inheriters(innerDecl, maxAllowedSteps);
0796     QCOMPARE(innerInheriters, QList<Declaration*>() << inheritedDecl);
0797 
0798     maxAllowedSteps = uint(-1);
0799     auto inheritedInheriters = DUChainUtils::inheriters(inheritedDecl, maxAllowedSteps);
0800     QCOMPARE(inheritedInheriters.count(), 0);
0801 }
0802 
0803 void TestDUChain::testGlobalFunctionDeclaration()
0804 {
0805     TestFile file(QStringLiteral("void foo(int arg1, char arg2);\n"), QStringLiteral("cpp"));
0806     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
0807     file.waitForParsed();
0808 
0809     DUChainReadLocker lock;
0810     QVERIFY(file.topContext());
0811     QCOMPARE(file.topContext()->localDeclarations().size(), 1);
0812     QCOMPARE(file.topContext()->childContexts().size(), 1);
0813     QVERIFY(!file.topContext()->childContexts().first()->inSymbolTable());
0814 }
0815 
0816 void TestDUChain::testFunctionDefinitionVsDeclaration()
0817 {
0818     TestFile file(QStringLiteral("void func(); void func() {}\n"), QStringLiteral("cpp"));
0819     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
0820     QVERIFY(file.waitForParsed());
0821 
0822     DUChainReadLocker lock;
0823     QVERIFY(file.topContext());
0824     QCOMPARE(file.topContext()->localDeclarations().size(), 2);
0825     auto funcDecl = file.topContext()->localDeclarations()[0];
0826     QVERIFY(!funcDecl->isDefinition());
0827     QVERIFY(!dynamic_cast<FunctionDefinition*>(funcDecl));
0828     auto funcDef = file.topContext()->localDeclarations()[1];
0829     QVERIFY(dynamic_cast<FunctionDefinition*>(funcDef));
0830     QVERIFY(funcDef->isDefinition());
0831 }
0832 
0833 void TestDUChain::testEnsureNoDoubleVisit()
0834 {
0835     // On some language construct, we may up visiting the same cursor multiple times
0836     // Example: "struct SomeStruct {} s;"
0837     // decl: "SomeStruct SomeStruct " of kind StructDecl (2) in main.cpp@[(1,1),(1,17)]
0838     // decl: "struct SomeStruct s " of kind VarDecl (9) in main.cpp@[(1,1),(1,19)]
0839     // decl: "SomeStruct SomeStruct " of kind StructDecl (2) in main.cpp@[(1,1),(1,17)]
0840     //
0841     // => We end up visiting the StructDecl twice (or more)
0842     //    That's because we use clang_visitChildren not just on the translation unit cursor.
0843     //    Apparently just "recursing" vs. "visiting children explicitly"
0844     //    results in a different AST traversal
0845 
0846     TestFile file(QStringLiteral("struct SomeStruct {} s;\n"), QStringLiteral("cpp"));
0847     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
0848     QVERIFY(file.waitForParsed());
0849 
0850     DUChainReadLocker lock;
0851     auto top = file.topContext();
0852     QVERIFY(top);
0853 
0854     // there should only be one declaration for "SomeStruct"
0855     auto candidates = top->findDeclarations(QualifiedIdentifier(QStringLiteral("SomeStruct")));
0856     QCOMPARE(candidates.size(), 1);
0857 }
0858 
0859 void TestDUChain::testParsingEnvironment()
0860 {
0861     const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses;
0862 
0863     IndexedTopDUContext indexed;
0864     ClangParsingEnvironment lastEnv;
0865     {
0866         TestFile file(QStringLiteral("int main() {}\n"), QStringLiteral("cpp"));
0867         const auto astFeatures = features | TopDUContext::AST;
0868         file.parse(astFeatures);
0869         file.setKeepDUChainData(true);
0870         QVERIFY(file.waitForParsed());
0871 
0872         DUChainWriteLocker lock;
0873         auto top = file.topContext();
0874         QVERIFY(top);
0875         auto sessionData = ParseSessionData::Ptr(dynamic_cast<ParseSessionData*>(top->ast().data()));
0876         lock.unlock();
0877         ParseSession session(sessionData);
0878         lock.lock();
0879         QVERIFY(session.data());
0880         QVERIFY(top);
0881 
0882         auto envFile = QExplicitlySharedDataPointer<ClangParsingEnvironmentFile>(
0883             dynamic_cast<ClangParsingEnvironmentFile*>(file.topContext()->parsingEnvironmentFile().data()));
0884 
0885         QCOMPARE(envFile->features(), astFeatures);
0886         QVERIFY(envFile->featuresSatisfied(astFeatures));
0887         QCOMPARE(envFile->environmentQuality(), ClangParsingEnvironment::Source);
0888 
0889         // if no environment is given, no update should be triggered
0890         QVERIFY(!envFile->needsUpdate());
0891 
0892         // same env should also not trigger a reparse
0893         ClangParsingEnvironment env = session.environment();
0894         QCOMPARE(env.quality(), ClangParsingEnvironment::Source);
0895         QVERIFY(!envFile->needsUpdate(&env));
0896 
0897         // but changing the environment should trigger an update
0898         env.addIncludes(Path::List() << Path(QStringLiteral("/foo/bar/baz")));
0899         QVERIFY(envFile->needsUpdate(&env));
0900         envFile->setEnvironment(env);
0901         QVERIFY(!envFile->needsUpdate(&env));
0902 
0903         // setting the environment quality higher should require an update
0904         env.setQuality(ClangParsingEnvironment::BuildSystem);
0905         QVERIFY(envFile->needsUpdate(&env));
0906         envFile->setEnvironment(env);
0907         QVERIFY(!envFile->needsUpdate(&env));
0908 
0909         // changing defines requires an update
0910         env.addDefines(QHash<QString, QString>{ { "foo", "bar" } });
0911         QVERIFY(envFile->needsUpdate(&env));
0912 
0913         // but only when changing the defines for the envFile's TU
0914         const auto barTU = IndexedString("bar.cpp");
0915         const auto oldTU = env.translationUnitUrl();
0916         env.setTranslationUnitUrl(barTU);
0917         QCOMPARE(env.translationUnitUrl(), barTU);
0918         QVERIFY(!envFile->needsUpdate(&env));
0919         env.setTranslationUnitUrl(oldTU);
0920         QVERIFY(envFile->needsUpdate(&env));
0921 
0922         // update it again
0923         envFile->setEnvironment(env);
0924         QVERIFY(!envFile->needsUpdate(&env));
0925         lastEnv = env;
0926 
0927         // now compare against a lower quality environment
0928         // in such a case, we do not want to trigger an update
0929         env.setQuality(ClangParsingEnvironment::Unknown);
0930         env.setTranslationUnitUrl(barTU);
0931         QVERIFY(!envFile->needsUpdate(&env));
0932 
0933         // even when the environment changes
0934         env.addIncludes(Path::List() << Path(QStringLiteral("/lalalala")));
0935         QVERIFY(!envFile->needsUpdate(&env));
0936 
0937         indexed = top->indexed();
0938     }
0939 
0940     DUChain::self()->storeToDisk();
0941 
0942     {
0943         DUChainWriteLocker lock;
0944         QVERIFY(!DUChain::self()->isInMemory(indexed.index()));
0945         QVERIFY(indexed.data());
0946         QVERIFY(DUChain::self()->environmentFileForDocument(indexed));
0947         auto envFile = QExplicitlySharedDataPointer<ClangParsingEnvironmentFile>(
0948             dynamic_cast<ClangParsingEnvironmentFile*>(DUChain::self()->environmentFileForDocument(indexed).data()));
0949         QVERIFY(envFile);
0950 
0951         QCOMPARE(envFile->features(), features);
0952         QVERIFY(envFile->featuresSatisfied(features));
0953         QVERIFY(!envFile->needsUpdate(&lastEnv));
0954         DUChain::self()->removeDocumentChain(indexed.data());
0955     }
0956 }
0957 
0958 void TestDUChain::testActiveDocumentHasASTAttached()
0959 {
0960   const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses;
0961 
0962     IndexedTopDUContext indexed;
0963     ClangParsingEnvironment lastEnv;
0964     {
0965         TestFile file(QStringLiteral("int main() {}\n"), QStringLiteral("cpp"));
0966         const auto astFeatures = features | TopDUContext::AST;
0967         file.parse(astFeatures);
0968         file.setKeepDUChainData(true);
0969         QVERIFY(file.waitForParsed());
0970 
0971         DUChainWriteLocker lock;
0972         auto top = file.topContext();
0973         QVERIFY(top);
0974         auto sessionData = ParseSessionData::Ptr(dynamic_cast<ParseSessionData*>(top->ast().data()));
0975         lock.unlock();
0976         ParseSession session(sessionData);
0977         lock.lock();
0978         QVERIFY(session.data());
0979         QVERIFY(top);
0980         QVERIFY(top->ast());
0981 
0982         indexed = top->indexed();
0983     }
0984 
0985     DUChain::self()->storeToDisk();
0986 
0987     {
0988         DUChainWriteLocker lock;
0989         QVERIFY(!DUChain::self()->isInMemory(indexed.index()));
0990         QVERIFY(indexed.data());
0991     }
0992 
0993     QUrl url;
0994     {
0995         DUChainReadLocker lock;
0996         auto ctx = indexed.data();
0997         QVERIFY(ctx);
0998         QVERIFY(!ctx->ast());
0999         url = ctx->url().toUrl();
1000     }
1001 
1002     QVERIFY(!QFileInfo::exists(url.toLocalFile()));
1003     QFile file(url.toLocalFile());
1004     file.open(QIODevice::WriteOnly);
1005     Q_ASSERT(file.isOpen());
1006 
1007     auto document = ICore::self()->documentController()->openDocument(url);
1008     QVERIFY(document);
1009     ICore::self()->documentController()->activateDocument(document);
1010 
1011     QApplication::processEvents();
1012     ICore::self()->languageController()->backgroundParser()->parseDocuments();
1013     QThread::sleep(1);
1014 
1015     document->close(KDevelop::IDocument::Discard);
1016     {
1017         DUChainReadLocker lock;
1018         auto ctx = indexed.data();
1019         QVERIFY(ctx);
1020         QVERIFY(ctx->ast());
1021     }
1022 
1023     DUChainWriteLocker lock;
1024     DUChain::self()->removeDocumentChain(indexed.data());
1025 }
1026 
1027 void TestDUChain::testActiveDocumentsGetBestPriority()
1028 {
1029     // note: this test would make more sense in kdevplatform, but we don't have a language plugin available there
1030     // (required for background parsing)
1031     // TODO: Create a fake-language plugin in kdevplatform for testing purposes, use that.
1032 
1033     TestFile file1(QStringLiteral("int main() {}\n"), QStringLiteral("cpp"));
1034     TestFile file2(QStringLiteral("int main() {}\n"), QStringLiteral("cpp"));
1035     TestFile file3(QStringLiteral("int main() {}\n"), QStringLiteral("cpp"));
1036 
1037     DUChain::self()->storeToDisk();
1038 
1039     auto backgroundParser = ICore::self()->languageController()->backgroundParser();
1040     QVERIFY(!backgroundParser->isQueued(file1.url()));
1041 
1042     auto documentController = ICore::self()->documentController();
1043 
1044     // open first document (no activation)
1045     auto doc = documentController->openDocument(file1.url().toUrl(), KTextEditor::Range::invalid(), {IDocumentController::DoNotActivate});
1046     QVERIFY(doc);
1047     QVERIFY(backgroundParser->isQueued(file1.url()));
1048     QCOMPARE(backgroundParser->priorityForDocument(file1.url()), (int)BackgroundParser::NormalPriority);
1049 
1050     // open second document, activate
1051     doc = documentController->openDocument(file2.url().toUrl());
1052     QVERIFY(doc);
1053     QVERIFY(backgroundParser->isQueued(file2.url()));
1054     QCOMPARE(backgroundParser->priorityForDocument(file2.url()), (int)BackgroundParser::BestPriority);
1055 
1056     // open third document, activate, too
1057     doc = documentController->openDocument(file3.url().toUrl());
1058     QVERIFY(doc);
1059     QVERIFY(backgroundParser->isQueued(file3.url()));
1060     QCOMPARE(backgroundParser->priorityForDocument(file3.url()), (int)BackgroundParser::BestPriority);
1061 }
1062 
1063 void TestDUChain::testSystemIncludes()
1064 {
1065     ClangParsingEnvironment env;
1066 
1067     Path::List projectIncludes = {
1068         Path("/projects/1"),
1069         Path("/projects/1/sub"),
1070         Path("/projects/2"),
1071         Path("/projects/2/sub")
1072     };
1073     env.addIncludes(projectIncludes);
1074     auto includes = env.includes();
1075     // no project paths set, so everything is considered a system include
1076     QCOMPARE(includes.system, projectIncludes);
1077     QVERIFY(includes.project.isEmpty());
1078 
1079     Path::List systemIncludes = {
1080         Path("/sys"),
1081         Path("/sys/sub")
1082     };
1083     env.addIncludes(systemIncludes);
1084     includes = env.includes();
1085     QCOMPARE(includes.system, projectIncludes + systemIncludes);
1086     QVERIFY(includes.project.isEmpty());
1087 
1088     Path::List projects = {
1089         Path("/projects/1"),
1090         Path("/projects/2")
1091     };
1092     env.setProjectPaths(projects);
1093     // now the list should be properly separated
1094     QCOMPARE(env.projectPaths(), projects);
1095     includes = env.includes();
1096     QCOMPARE(includes.system, systemIncludes);
1097     QCOMPARE(includes.project, projectIncludes);
1098 }
1099 
1100 void TestDUChain::testReparseWithAllDeclarationsContextsAndUses()
1101 {
1102     TestFile file(QStringLiteral("int foo() { return 0; } int main() { return foo(); }"), QStringLiteral("cpp"));
1103     file.parse(TopDUContext::VisibleDeclarationsAndContexts);
1104 
1105     QVERIFY(file.waitForParsed(1000));
1106 
1107     {
1108         DUChainReadLocker lock;
1109         QVERIFY(file.topContext());
1110         QCOMPARE(file.topContext()->childContexts().size(), 2);
1111         QCOMPARE(file.topContext()->localDeclarations().size(), 2);
1112 
1113         auto dec = file.topContext()->localDeclarations().at(0);
1114         QEXPECT_FAIL("", "Skipping of function bodies is disabled for now", Continue);
1115         QVERIFY(dec->uses().isEmpty());
1116     }
1117 
1118     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1119 
1120     QVERIFY(file.waitForParsed(500));
1121 
1122     {
1123         DUChainReadLocker lock;
1124         QVERIFY(file.topContext());
1125         QCOMPARE(file.topContext()->childContexts().size(), 2);
1126         QCOMPARE(file.topContext()->localDeclarations().size(), 2);
1127 
1128         auto mainDecl = file.topContext()->localDeclarations()[1];
1129         QVERIFY(mainDecl->uses().isEmpty());
1130         auto foo = file.topContext()->localDeclarations().first();
1131         QCOMPARE(foo->uses().size(), 1);
1132     }
1133 }
1134 
1135 void TestDUChain::testReparseOnDocumentActivated()
1136 {
1137     TestFile file(QStringLiteral("int foo() { return 0; } int main() { return foo(); }"), QStringLiteral("cpp"));
1138     file.parse(TopDUContext::VisibleDeclarationsAndContexts);
1139 
1140     QVERIFY(file.waitForParsed(1000));
1141 
1142     {
1143         DUChainReadLocker lock;
1144         auto ctx = file.topContext();
1145         QVERIFY(ctx);
1146         QCOMPARE(ctx->childContexts().size(), 2);
1147         QCOMPARE(ctx->localDeclarations().size(), 2);
1148 
1149         auto dec = ctx->localDeclarations().at(0);
1150         QEXPECT_FAIL("", "Skipping of function bodies was disabled for now", Continue);
1151         QVERIFY(dec->uses().isEmpty());
1152 
1153         QVERIFY(!ctx->ast());
1154     }
1155 
1156     auto backgroundParser = ICore::self()->languageController()->backgroundParser();
1157     QVERIFY(!backgroundParser->isQueued(file.url()));
1158 
1159     auto doc = ICore::self()->documentController()->openDocument(file.url().toUrl());
1160     QVERIFY(doc);
1161     QVERIFY(backgroundParser->isQueued(file.url()));
1162 
1163     QSignalSpy spy(backgroundParser, &BackgroundParser::parseJobFinished);
1164     spy.wait();
1165 
1166     doc->close(KDevelop::IDocument::Discard);
1167 
1168     {
1169         DUChainReadLocker lock;
1170         auto ctx = file.topContext();
1171         QCOMPARE(ctx->features() & TopDUContext::AllDeclarationsContextsAndUses,
1172                  TopDUContext::AllDeclarationsContextsAndUses);
1173         QVERIFY(ctx->topContext()->ast());
1174     }
1175 }
1176 
1177 void TestDUChain::testReparseInclude()
1178 {
1179     TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h"));
1180     TestFile impl("#include \"" + header.url().str() + "\"\n"
1181                   "int main() { return foo(); }", QStringLiteral("cpp"), &header);
1182 
1183     // Use TopDUContext::AST to imitate that document is opened in the editor, so that ClangParseJob can store translation unit, that'll be used for reparsing.
1184     impl.parse(TopDUContext::AllDeclarationsAndContexts | TopDUContext::AST);
1185     QVERIFY(impl.waitForParsed(5000));
1186     {
1187         DUChainReadLocker lock;
1188         auto implCtx = impl.topContext();
1189         QVERIFY(implCtx);
1190         QCOMPARE(implCtx->importedParentContexts().size(), 1);
1191     }
1192 
1193     impl.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST);
1194     QVERIFY(impl.waitForParsed(5000));
1195 
1196     DUChainReadLocker lock;
1197     auto implCtx = impl.topContext();
1198     QVERIFY(implCtx);
1199     QCOMPARE(implCtx->localDeclarations().size(), 1);
1200 
1201     QCOMPARE(implCtx->importedParentContexts().size(), 1);
1202 
1203     auto headerCtx = DUChain::self()->chainForDocument(header.url());
1204     QVERIFY(headerCtx);
1205     QVERIFY(!headerCtx->parsingEnvironmentFile()->needsUpdate());
1206     QCOMPARE(headerCtx->localDeclarations().size(), 1);
1207 
1208     QVERIFY(implCtx->imports(headerCtx, CursorInRevision(0, 10)));
1209 
1210     Declaration* foo = headerCtx->localDeclarations().first();
1211     QCOMPARE(foo->uses().size(), 1);
1212     QCOMPARE(foo->uses().begin().key(), impl.url());
1213     QCOMPARE(foo->uses().begin()->size(), 1);
1214     QCOMPARE(foo->uses().begin()->first(), RangeInRevision(1, 20, 1, 23));
1215 
1216     QCOMPARE(DUChain::self()->allEnvironmentFiles(header.url()).size(), 1);
1217     QCOMPARE(DUChain::self()->allEnvironmentFiles(impl.url()).size(), 1);
1218     QCOMPARE(DUChain::self()->chainsForDocument(header.url()).size(), 1);
1219     QCOMPARE(DUChain::self()->chainsForDocument(impl.url()).size(), 1);
1220 }
1221 
1222 void TestDUChain::testReparseChangeEnvironment()
1223 {
1224     TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h"));
1225     TestFile impl("#include \"" + header.url().str() + "\"\n"
1226                   "int main() { return foo(); }", QStringLiteral("cpp"), &header);
1227 
1228     uint hashes[3] = {0, 0, 0};
1229 
1230     for (int i = 0; i < 3; ++i) {
1231         impl.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST | TopDUContext::ForceUpdate);
1232         QVERIFY(impl.waitForParsed(5000));
1233 
1234         {
1235             DUChainReadLocker lock;
1236             QVERIFY(impl.topContext());
1237             auto env = dynamic_cast<ClangParsingEnvironmentFile*>(impl.topContext()->parsingEnvironmentFile().data());
1238             QVERIFY(env);
1239             QCOMPARE(env->environmentQuality(), ClangParsingEnvironment::Source);
1240             hashes[i] = env->environmentHash();
1241             QVERIFY(hashes[i]);
1242 
1243             // we should never end up with multiple env files or chains in memory for these files
1244             QCOMPARE(DUChain::self()->allEnvironmentFiles(impl.url()).size(), 1);
1245             QCOMPARE(DUChain::self()->chainsForDocument(impl.url()).size(), 1);
1246             QCOMPARE(DUChain::self()->allEnvironmentFiles(header.url()).size(), 1);
1247             QCOMPARE(DUChain::self()->chainsForDocument(header.url()).size(), 1);
1248         }
1249 
1250         // in every run, we expect the environment to have changed
1251         for (int j = 0; j < i; ++j) {
1252             QVERIFY(hashes[i] != hashes[j]);
1253         }
1254 
1255         if (i == 0) {
1256             // 1) change defines
1257             m_provider->defines.insert(QStringLiteral("foooooooo"), QStringLiteral("baaar!"));
1258         } else if (i == 1) {
1259             // 2) change includes
1260             m_provider->includes.append(Path(QStringLiteral("/foo/bar/asdf/lalala")));
1261         } // 3) stop
1262     }
1263 }
1264 
1265 void TestDUChain::testMacroDependentHeader()
1266 {
1267     TestFile header(QStringLiteral("struct MY_CLASS { struct Q{Q(); int m;}; int m; };\n"), QStringLiteral("h"));
1268     TestFile impl("#define MY_CLASS A\n"
1269                   "#include \"" + header.url().str() + "\"\n"
1270                   "#undef MY_CLASS\n"
1271                   "#define MY_CLASS B\n"
1272                   "#include \"" + header.url().str() + "\"\n"
1273                   "#undef MY_CLASS\n"
1274                   "A a;\n"
1275                   "const A::Q aq;\n"
1276                   "B b;\n"
1277                   "const B::Q bq;\n"
1278                   "int am = a.m;\n"
1279                   "int aqm = aq.m;\n"
1280                   "int bm = b.m;\n"
1281                   "int bqm = bq.m;\n"
1282                   , QStringLiteral("cpp"), &header);
1283 
1284     impl.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST | TopDUContext::ForceUpdate);
1285     QVERIFY(impl.waitForParsed(500000));
1286 
1287     DUChainReadLocker lock;
1288     TopDUContext* top = impl.topContext().data();
1289     QVERIFY(top);
1290     QCOMPARE(top->localDeclarations().size(), 10); // 2x macro, then a, aq, b, bq
1291     QCOMPARE(top->importedParentContexts().size(), 1);
1292     AbstractType::Ptr type = top->localDeclarations()[2]->abstractType();
1293     auto* sType = dynamic_cast<StructureType*>(type.data());
1294     QVERIFY(sType);
1295     QCOMPARE(sType->toString(), QString("A"));
1296     Declaration* decl = sType->declaration(top);
1297     QVERIFY(decl);
1298     AbstractType::Ptr type2 = top->localDeclarations()[4]->abstractType();
1299     auto* sType2 = dynamic_cast<StructureType*>(type2.data());
1300     QVERIFY(sType2);
1301     QCOMPARE(sType2->toString(), QString("B"));
1302     Declaration* decl2 = sType2->declaration(top);
1303     QVERIFY(decl2);
1304 
1305     TopDUContext* top2 = dynamic_cast<TopDUContext*>(top->importedParentContexts()[0].context(top));
1306     QVERIFY(top2);
1307     QCOMPARE(top2->localDeclarations().size(), 2);
1308     QCOMPARE(top2->localDeclarations()[0], decl);
1309     QCOMPARE(top2->localDeclarations()[1], decl2);
1310     qDebug() << "DECL RANGE:" << top2->localDeclarations()[0]->range().castToSimpleRange();
1311     qDebug() << "CTX RANGE:" << top2->localDeclarations()[0]->internalContext()->range().castToSimpleRange();
1312 
1313     // validate uses:
1314     QCOMPARE(top->usesCount(), 14);
1315     QCOMPARE(top->uses()[0].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"A"));
1316     QCOMPARE(top->uses()[1].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"A"));
1317     QCOMPARE(top->uses()[2].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"A::Q"));
1318     QCOMPARE(top->uses()[3].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"B"));
1319     QCOMPARE(top->uses()[4].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"B"));
1320     QCOMPARE(top->uses()[5].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"B::Q"));
1321     QCOMPARE(top->uses()[6].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
1322     QCOMPARE(top->uses()[7].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"A::m"));
1323     QCOMPARE(top->uses()[8].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"aq"));
1324     QCOMPARE(top->uses()[9].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"A::Q::m"));
1325     QCOMPARE(top->uses()[10].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1326     QCOMPARE(top->uses()[11].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"B::m"));
1327     QCOMPARE(top->uses()[12].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"bq"));
1328     QCOMPARE(top->uses()[13].usedDeclaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"B::Q::m"));
1329 }
1330 
1331 void TestDUChain::testHeaderParsingOrder1()
1332 {
1333     TestFile header(QStringLiteral("typedef const A<int> B;\n"), QStringLiteral("h"));
1334     TestFile impl("template<class T> class A{};\n"
1335                   "#include \"" + header.url().str() + "\"\n"
1336                   "B c;", QStringLiteral("cpp"), &header);
1337 
1338     impl.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST | TopDUContext::ForceUpdate);
1339     QVERIFY(impl.waitForParsed(500000));
1340 
1341     DUChainReadLocker lock;
1342     TopDUContext* top = impl.topContext().data();
1343     QVERIFY(top);
1344     QCOMPARE(top->localDeclarations().size(), 2);
1345     QCOMPARE(top->importedParentContexts().size(), 1);
1346     AbstractType::Ptr type = top->localDeclarations()[1]->abstractType();
1347     auto* aType = dynamic_cast<TypeAliasType*>(type.data());
1348     QVERIFY(aType);
1349     AbstractType::Ptr targetType = aType->type();
1350     QVERIFY(targetType);
1351     auto *idType = dynamic_cast<IdentifiedType*>(targetType.data());
1352     QVERIFY(idType);
1353     // this declaration could be resolved, because it was created with an
1354     // indirect DeclarationId that is resolved from the perspective of 'top'
1355     Declaration* decl = idType->declaration(top);
1356     // NOTE: the decl. doesn't know (yet) about the template insantiation <int>
1357     QVERIFY(decl);
1358     QCOMPARE(decl, top->localDeclarations()[0]);
1359 
1360     // now ensure that a use was build for 'A' in header1
1361     TopDUContext* top2 = dynamic_cast<TopDUContext*>(top->importedParentContexts()[0].context(top));
1362     QVERIFY(top2);
1363     QEXPECT_FAIL("", "the use could not be created because the corresponding declaration didn't exist yet", Continue);
1364     QCOMPARE(top2->usesCount(), 1);
1365     // Declaration* decl2 = top2->uses()[0].usedDeclaration(top2);
1366     // QVERIFY(decl2);
1367     // QCOMPARE(decl, decl2);
1368 }
1369 
1370 void TestDUChain::testHeaderParsingOrder2()
1371 {
1372     TestFile header(QStringLiteral("template<class T> class A{};\n"), QStringLiteral("h"));
1373     TestFile header2(QStringLiteral("typedef const A<int> B;\n"), QStringLiteral("h"));
1374     TestFile impl("#include \"" + header.url().str() + "\"\n"
1375                   "#include \"" + header2.url().str() + "\"\n"
1376                   "B c;", QStringLiteral("cpp"), &header);
1377 
1378     impl.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST | TopDUContext::ForceUpdate);
1379     QVERIFY(impl.waitForParsed(500000));
1380 
1381     DUChainReadLocker lock;
1382     TopDUContext* top = impl.topContext().data();
1383     QVERIFY(top);
1384     QCOMPARE(top->localDeclarations().size(), 1);
1385     QCOMPARE(top->importedParentContexts().size(), 2);
1386     AbstractType::Ptr type = top->localDeclarations()[0]->abstractType();
1387     auto* aType = dynamic_cast<TypeAliasType*>(type.data());
1388     QVERIFY(aType);
1389     AbstractType::Ptr targetType = aType->type();
1390     QVERIFY(targetType);
1391     auto *idType = dynamic_cast<IdentifiedType*>(targetType.data());
1392     QVERIFY(idType);
1393     Declaration* decl = idType->declaration(top);
1394     // NOTE: the decl. doesn't know (yet) about the template insantiation <int>
1395     QVERIFY(decl);
1396 
1397     // now ensure that a use was build for 'A' in header2
1398     TopDUContext* top2 = dynamic_cast<TopDUContext*>(top->importedParentContexts()[1].context(top));
1399     QVERIFY(top2);
1400     QCOMPARE(top2->usesCount(), 1);
1401     Declaration* decl2 = top2->uses()[0].usedDeclaration(top2);
1402     QCOMPARE(decl, decl2);
1403 }
1404 
1405 void TestDUChain::testMacrosRanges()
1406 {
1407     TestFile file(QStringLiteral("#define FUNC_MACROS(x) struct str##x{};\nFUNC_MACROS(x);"), QStringLiteral("cpp"));
1408     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1409     QVERIFY(file.waitForParsed(5000));
1410 
1411     DUChainReadLocker lock;
1412     QVERIFY(file.topContext());
1413     QCOMPARE(file.topContext()->localDeclarations().size(), 2);
1414     auto macroDefinition = file.topContext()->localDeclarations()[0];
1415     QVERIFY(macroDefinition);
1416     QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,19));
1417     auto structDeclaration = file.topContext()->localDeclarations()[1];
1418     QVERIFY(structDeclaration);
1419     QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0));
1420 
1421     QCOMPARE(macroDefinition->uses().size(), 1);
1422     QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,11));
1423 }
1424 
1425 void TestDUChain::testMacroUses()
1426 {
1427     TestFile file(QStringLiteral("#define USER(x) x\n#define USED\nUSER(USED)"), QStringLiteral("cpp"));
1428     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1429     QVERIFY(file.waitForParsed(5000));
1430 
1431     DUChainReadLocker lock;
1432     QVERIFY(file.topContext());
1433     QCOMPARE(file.topContext()->localDeclarations().size(), 2);
1434     auto macroDefinition1 = file.topContext()->localDeclarations()[0];
1435     auto macroDefinition2 = file.topContext()->localDeclarations()[1];
1436 
1437     QCOMPARE(macroDefinition1->uses().size(), 1);
1438     QCOMPARE(macroDefinition1->uses().begin()->first(), RangeInRevision(2,0,2,4));
1439 #if CINDEX_VERSION_MINOR < 32
1440     QEXPECT_FAIL("", "This appears to be a clang bug, the AST doesn't contain the macro use", Continue);
1441 #endif
1442     QCOMPARE(macroDefinition2->uses().size(), 1);
1443     if (macroDefinition2->uses().size())
1444     {
1445         QCOMPARE(macroDefinition2->uses().begin()->first(), RangeInRevision(2,5,2,9));
1446     }
1447 }
1448 
1449 void TestDUChain::testMultiLineMacroRanges()
1450 {
1451     TestFile file(QStringLiteral("#define FUNC_MACROS(x) struct str##x{};\nFUNC_MACROS(x\n);"), QStringLiteral("cpp"));
1452     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1453     QVERIFY(file.waitForParsed(5000));
1454 
1455     DUChainReadLocker lock;
1456     QVERIFY(file.topContext());
1457     QCOMPARE(file.topContext()->localDeclarations().size(), 2);
1458     auto macroDefinition = file.topContext()->localDeclarations()[0];
1459     QVERIFY(macroDefinition);
1460     QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,19));
1461     auto structDeclaration = file.topContext()->localDeclarations()[1];
1462     QVERIFY(structDeclaration);
1463     QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0));
1464 
1465     QCOMPARE(macroDefinition->uses().size(), 1);
1466     QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,11));
1467 }
1468 
1469 void TestDUChain::testNestedMacroRanges()
1470 {
1471     TestFile file(QStringLiteral("#define INNER int var; var = 0;\n#define MACRO() INNER\nint main(){MACRO(\n);}"), QStringLiteral("cpp"));
1472     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1473     QVERIFY(file.waitForParsed(5000));
1474 
1475     DUChainReadLocker lock;
1476     QVERIFY(file.topContext());
1477     QCOMPARE(file.topContext()->localDeclarations().size(), 3);
1478     auto main = file.topContext()->localDeclarations()[2];
1479     QVERIFY(main);
1480     auto mainCtx = main->internalContext()->childContexts().first();
1481     QVERIFY(mainCtx);
1482     QCOMPARE(mainCtx->localDeclarations().size(), 1);
1483     auto var = mainCtx->localDeclarations().first();
1484     QVERIFY(var);
1485     QCOMPARE(var->range(), RangeInRevision(2,11,2,11));
1486 
1487     QCOMPARE(var->uses().size(), 1);
1488     QCOMPARE(var->uses().begin()->first(), RangeInRevision(2,11,2,11));
1489 }
1490 
1491 void TestDUChain::testNestedImports()
1492 {
1493     TestFile B(QStringLiteral("#pragma once\nint B();\n"), QStringLiteral("h"));
1494     TestFile C("#pragma once\n#include \"" + B.url().str() + "\"\nint C();\n", QStringLiteral("h"));
1495     TestFile A("#include \"" + B.url().str() + "\"\n" + "#include \"" + C.url().str() + "\"\nint A();\n", QStringLiteral("cpp"));
1496 
1497     A.parse();
1498     QVERIFY(A.waitForParsed(5000));
1499 
1500     DUChainReadLocker lock;
1501 
1502     auto BCtx = DUChain::self()->chainForDocument(B.url().toUrl());
1503     QVERIFY(BCtx);
1504     QVERIFY(BCtx->importedParentContexts().isEmpty());
1505 
1506     auto CCtx = DUChain::self()->chainForDocument(C.url().toUrl());
1507     QVERIFY(CCtx);
1508     QCOMPARE(CCtx->importedParentContexts().size(), 1);
1509     QVERIFY(CCtx->imports(BCtx, CursorInRevision(1, 10)));
1510 
1511     auto ACtx = A.topContext();
1512     QVERIFY(ACtx);
1513     QCOMPARE(ACtx->importedParentContexts().size(), 2);
1514     QVERIFY(ACtx->imports(BCtx, CursorInRevision(0, 10)));
1515     QVERIFY(ACtx->imports(CCtx, CursorInRevision(1, 10)));
1516 }
1517 
1518 void TestDUChain::testEnvironmentWithDifferentOrderOfElements()
1519 {
1520     TestFile file(QStringLiteral("int main();\n"), QStringLiteral("cpp"));
1521 
1522     m_provider->includes.clear();
1523     m_provider->includes.append(Path(QStringLiteral("/path1")));
1524     m_provider->includes.append(Path(QStringLiteral("/path2")));
1525 
1526     m_provider->defines.clear();
1527     m_provider->defines.insert(QStringLiteral("key1"), QStringLiteral("value1"));
1528     m_provider->defines.insert(QStringLiteral("key2"), QStringLiteral("value2"));
1529     m_provider->defines.insert(QStringLiteral("key3"), QStringLiteral("value3"));
1530 
1531     uint previousHash = 0;
1532     for (int i: {0, 1, 2, 3}) {
1533         file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST | TopDUContext::ForceUpdate);
1534 
1535         QVERIFY(file.waitForParsed(5000));
1536 
1537         {
1538             DUChainReadLocker lock;
1539             QVERIFY(file.topContext());
1540             auto env = dynamic_cast<ClangParsingEnvironmentFile*>(file.topContext()->parsingEnvironmentFile().data());
1541             QVERIFY(env);
1542             QCOMPARE(env->environmentQuality(), ClangParsingEnvironment::Source);
1543             if (previousHash) {
1544                 if (i == 3) {
1545                     QVERIFY(previousHash != env->environmentHash());
1546                 } else {
1547                     QCOMPARE(previousHash, env->environmentHash());
1548                 }
1549             }
1550             previousHash = env->environmentHash();
1551             QVERIFY(previousHash);
1552         }
1553 
1554         if (i == 0) {
1555             //Change order of defines. Hash of the environment should stay the same.
1556             m_provider->defines.clear();
1557             m_provider->defines.insert(QStringLiteral("key3"), QStringLiteral("value3"));
1558             m_provider->defines.insert(QStringLiteral("key1"), QStringLiteral("value1"));
1559             m_provider->defines.insert(QStringLiteral("key2"), QStringLiteral("value2"));
1560         } else if (i == 1) {
1561             //Add the same macros twice. Hash of the environment should stay the same.
1562             m_provider->defines.clear();
1563             m_provider->defines.insert(QStringLiteral("key2"), QStringLiteral("value2"));
1564             m_provider->defines.insert(QStringLiteral("key3"), QStringLiteral("value3"));
1565             m_provider->defines.insert(QStringLiteral("key3"), QStringLiteral("value3"));
1566             m_provider->defines.insert(QStringLiteral("key1"), QStringLiteral("value1"));
1567         } else if (i == 2) {
1568             //OTOH order of includes should change hash of the environment.
1569             m_provider->includes.clear();
1570             m_provider->includes.append(Path(QStringLiteral("/path2")));
1571             m_provider->includes.append(Path(QStringLiteral("/path1")));
1572         }
1573     }
1574 }
1575 
1576 void TestDUChain::testReparseMacro()
1577 {
1578     TestFile file(QStringLiteral("#define DECLARE(a) typedef struct a##_ {} *a;\nDECLARE(D);\nD d;"), QStringLiteral("cpp"));
1579     file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST);
1580     QVERIFY(file.waitForParsed(5000));
1581 
1582     {
1583         DUChainReadLocker lock;
1584         QVERIFY(file.topContext());
1585     }
1586 
1587     file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST | TopDUContext::ForceUpdate);
1588     QVERIFY(file.waitForParsed(5000));
1589 
1590     DUChainReadLocker lock;
1591     QVERIFY(file.topContext());
1592     QCOMPARE(file.topContext()->localDeclarations().size(), 5);
1593 
1594     auto macroDefinition = file.topContext()->localDeclarations()[0];
1595     QVERIFY(macroDefinition);
1596     QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,15));
1597     QCOMPARE(macroDefinition->uses().size(), 1);
1598     QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,7));
1599 
1600     auto structDeclaration = file.topContext()->localDeclarations()[1];
1601     QVERIFY(structDeclaration);
1602     QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0));
1603 
1604     auto structTypedef = file.topContext()->localDeclarations()[3];
1605     QVERIFY(structTypedef);
1606     QCOMPARE(structTypedef->range(), RangeInRevision(1,8,1,9));
1607     QCOMPARE(structTypedef->uses().size(), 1);
1608     QCOMPARE(structTypedef->uses().begin()->first(), RangeInRevision(2,0,2,1));
1609 }
1610 
1611 void TestDUChain::testGotoStatement()
1612 {
1613     TestFile file(QStringLiteral("int main() {\ngoto label;\ngoto label;\nlabel: return 0;}"), QStringLiteral("cpp"));
1614     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1615     QVERIFY(file.waitForParsed(5000));
1616 
1617     DUChainReadLocker lock;
1618     QVERIFY(file.topContext());
1619     QCOMPARE(file.topContext()->localDeclarations().size(), 1);
1620     auto main = file.topContext()->localDeclarations()[0];
1621     QVERIFY(main);
1622     auto mainCtx = main->internalContext()->childContexts().first();
1623     QVERIFY(mainCtx);
1624     QCOMPARE(mainCtx->localDeclarations().size(), 1);
1625     auto label = mainCtx->localDeclarations().first();
1626     QVERIFY(label);
1627     QCOMPARE(label->range(), RangeInRevision(3,0,3,5));
1628 
1629     QCOMPARE(label->uses().size(), 1);
1630     QCOMPARE(label->uses().begin()->first(), RangeInRevision(1,5,1,10));
1631     QCOMPARE(label->uses().begin()->last(), RangeInRevision(2,5,2,10));
1632 }
1633 
1634 void TestDUChain::testRangesOfOperatorsInsideMacro()
1635 {
1636     TestFile file(QStringLiteral("class Test{public: Test& operator++(int);};\n#define MACRO(var) var++;\nint main(){\nTest tst; MACRO(tst)}"), QStringLiteral("cpp"));
1637     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1638     QVERIFY(file.waitForParsed(5000));
1639 
1640     DUChainReadLocker lock;
1641     QVERIFY(file.topContext());
1642     QCOMPARE(file.topContext()->localDeclarations().size(), 3);
1643     auto testClass = file.topContext()->localDeclarations()[0];
1644     QVERIFY(testClass);
1645     auto operatorPlusPlus = testClass->internalContext()->localDeclarations().first();
1646     QVERIFY(operatorPlusPlus);
1647     QCOMPARE(operatorPlusPlus->uses().size(), 1);
1648     QCOMPARE(operatorPlusPlus->uses().begin()->first(), RangeInRevision(3,10,3,10));
1649 }
1650 
1651 void TestDUChain::testUsesCreatedForDeclarations()
1652 {
1653     auto code = R"(template<typename T> void functionTemplate(T);
1654             template<typename U> void functionTemplate(U) {}
1655 
1656             namespace NS { class Class{}; }
1657             using NS::Class;
1658 
1659             Class function();
1660             NS::Class function() { return {}; }
1661 
1662             int main () {
1663                 functionTemplate(int());
1664                 function(); }
1665     )";
1666     TestFile file(code, QStringLiteral("cpp"));
1667     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1668     QVERIFY(file.waitForParsed());
1669 
1670     DUChainReadLocker lock;
1671     QVERIFY(file.topContext());
1672 
1673     auto functionTemplate = file.topContext()->findDeclarations(QualifiedIdentifier(QStringLiteral("functionTemplate")));
1674     QVERIFY(!functionTemplate.isEmpty());
1675     auto functionTemplateDeclaration = DUChainUtils::declarationForDefinition(functionTemplate.first());
1676     QVERIFY(!functionTemplateDeclaration->isDefinition());
1677 #if CINDEX_VERSION_MINOR < 29
1678     QEXPECT_FAIL("", "No API in LibClang to determine function template type", Continue);
1679 #endif
1680     QCOMPARE(functionTemplateDeclaration->uses().count(), 1);
1681 
1682     auto function = file.topContext()->findDeclarations(QualifiedIdentifier(QStringLiteral("function")));
1683     QVERIFY(!function.isEmpty());
1684     auto functionDeclaration = DUChainUtils::declarationForDefinition(function.first());
1685     QVERIFY(!functionDeclaration->isDefinition());
1686     QCOMPARE(functionDeclaration->uses().count(), 1);
1687 }
1688 
1689 void TestDUChain::testReparseIncludeGuard()
1690 {
1691     TestFile header(QStringLiteral("#ifndef GUARD\n#define GUARD\nint something;\n#endif\n"), QStringLiteral("h"));
1692     TestFile impl("#include \"" + header.url().str() + "\"\n", QStringLiteral("cpp"), &header);
1693 
1694     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST));
1695     {
1696         DUChainReadLocker lock;
1697         QCOMPARE(static_cast<TopDUContext*>(impl.topContext()->
1698             importedParentContexts().first().context(impl.topContext()))->problems().size(), 0);
1699     }
1700     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive));
1701     {
1702         DUChainReadLocker lock;
1703         QCOMPARE(static_cast<TopDUContext*>(impl.topContext()->
1704             importedParentContexts().first().context(impl.topContext()))->problems().size(), 0);
1705     }
1706 }
1707 
1708 void TestDUChain::testIncludeGuardHeaderHeaderOnly()
1709 {
1710     QFETCH(QString, code);
1711 
1712     // test that we do NOT get a warning for single standalone header file with include guards
1713     TestFile header(code, QStringLiteral("h"));
1714 
1715     QVERIFY(header.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST));
1716     {
1717         DUChainReadLocker lock;
1718         QCOMPARE(header.topContext()->problems().size(), 0);
1719     }
1720 }
1721 
1722 void TestDUChain::testIncludeGuardHeaderHeaderOnly_data()
1723 {
1724     QTest::addColumn<QString>("code");
1725 
1726     QTest::newRow("guard-ifdef") << QStringLiteral(
1727         "#ifndef GUARD\n"
1728         "#define GUARD\n"
1729         "int something;\n"
1730         "#endif\n"
1731     );
1732 
1733     QTest::newRow("guard-pragma") << QStringLiteral(
1734         "#pragma once\n"
1735         "int something;\n"
1736     );
1737 }
1738 
1739 void TestDUChain::testIncludeGuardHeaderWarning()
1740 {
1741     // test that we do get a warning for a header file without include guards
1742     TestFile header(QStringLiteral("int something;\n"), QStringLiteral("h"));
1743     TestFile impl("#include \"" + header.url().str() + "\"\n", QStringLiteral("cpp"));
1744 
1745     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST | TopDUContext::ForceUpdate));
1746     {
1747         DUChainReadLocker lock;
1748         QVERIFY(impl.topContext());
1749 
1750         auto context = DUChain::self()->chainForDocument(impl.url());
1751         QVERIFY(context);
1752         QCOMPARE(context->problems().size(), 0);
1753 
1754         context = DUChain::self()->chainForDocument(header.url());
1755         QVERIFY(context);
1756         QCOMPARE(context->problems().size(), 1);
1757     }
1758 }
1759 
1760 void TestDUChain::testExternC()
1761 {
1762     auto code = R"(extern "C" { void foo(); })";
1763     TestFile file(code, QStringLiteral("cpp"));
1764     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
1765     QVERIFY(file.waitForParsed());
1766 
1767     DUChainReadLocker lock;
1768     auto top = file.topContext();
1769     QVERIFY(top);
1770     QVERIFY(!top->findDeclarations(QualifiedIdentifier(u"foo")).isEmpty());
1771 }
1772 
1773 void TestDUChain::testIncludeExternC()
1774 {
1775     TestFile header(QStringLiteral("int foo() { return 42; }\n"), QStringLiteral("h"));
1776     // NOTE: header is _not_ explicitly being parsed, instead the impl job does that
1777 
1778     const auto code = R"(
1779         extern "C" {
1780             #include "%1"
1781         }
1782         int main() { return foo(); }
1783     )";
1784     TestFile impl(QString::fromUtf8(code).arg(header.url().str()), QStringLiteral("cpp"), &header);
1785     impl.parse(TopDUContext::AllDeclarationsContextsAndUses);
1786     QVERIFY(impl.waitForParsed());
1787 
1788     DUChainReadLocker lock;
1789 
1790     auto implCtx = impl.topContext();
1791     QVERIFY(implCtx);
1792     QCOMPARE(implCtx->localDeclarations().size(), 1);
1793 
1794     auto headerCtx = DUChain::self()->chainForDocument(header.url());
1795     QVERIFY(headerCtx);
1796     QCOMPARE(headerCtx->localDeclarations().size(), 1);
1797     Declaration* foo = headerCtx->localDeclarations().first();
1798     QCOMPARE(foo->uses().size(), 1);
1799     QCOMPARE(foo->uses().begin().key(), impl.url());
1800     QCOMPARE(foo->uses().begin()->size(), 1);
1801     QCOMPARE(foo->uses().begin()->first(), RangeInRevision(4, 28, 4, 31));
1802 }
1803 
1804 void TestDUChain::testReparseUnchanged_data()
1805 {
1806     QTest::addColumn<QString>("headerCode");
1807     QTest::addColumn<QString>("implCode");
1808 
1809     QTest::newRow("include-guards") << R"(
1810         #ifndef GUARD
1811         #define GUARD
1812         int something;
1813         #endif
1814     )" << R"(
1815         #include "%1"
1816     )";
1817 
1818     QTest::newRow("template-default-parameters") << R"(
1819         #ifndef TEST_H
1820         #define TEST_H
1821 
1822         template<unsigned T=123, unsigned... U>
1823         class dummy;
1824 
1825         template<unsigned T, unsigned... U>
1826         class dummy {
1827             int field[T];
1828         };
1829 
1830         #endif
1831     )" << R"(
1832         #include "%1"
1833 
1834         int main(int, char **) {
1835             dummy<> x;
1836             (void)x;
1837         }
1838     )";
1839 }
1840 
1841 void TestDUChain::testReparseUnchanged()
1842 {
1843     QFETCH(QString, headerCode);
1844     QFETCH(QString, implCode);
1845     TestFile header(headerCode, QStringLiteral("h"));
1846     TestFile impl(implCode.arg(header.url().str()), QStringLiteral("cpp"), &header);
1847 
1848     auto checkProblems = [&] (bool reparsed) {
1849         DUChainReadLocker lock;
1850         auto headerCtx = DUChain::self()->chainForDocument(header.url());
1851         QVERIFY(headerCtx);
1852         QVERIFY(headerCtx->problems().isEmpty());
1853         auto implCtx = DUChain::self()->chainForDocument(impl.url());
1854         QVERIFY(implCtx);
1855         if (reparsed && CINDEX_VERSION_MINOR > 29 && CINDEX_VERSION_MINOR < 33) {
1856             QEXPECT_FAIL("template-default-parameters", "the precompiled preamble messes the default template parameters up in clang 3.7", Continue);
1857         }
1858         QVERIFY(implCtx->problems().isEmpty());
1859     };
1860 
1861     impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST);
1862     checkProblems(false);
1863 
1864     impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive);
1865     checkProblems(true);
1866 }
1867 
1868 void TestDUChain::testTypeAliasTemplate()
1869 {
1870     TestFile file(QStringLiteral("template <typename T> using Alias = T; using Foo = Alias<int>;"), QStringLiteral("cpp"));
1871     QVERIFY(file.parseAndWait());
1872 
1873     DUChainReadLocker lock;
1874     QVERIFY(file.topContext());
1875 
1876     QCOMPARE(file.topContext()->localDeclarations().size(), 2);
1877     auto templateAlias = file.topContext()->localDeclarations().first();
1878     QVERIFY(templateAlias);
1879 #if CINDEX_VERSION_MINOR < 31
1880     QEXPECT_FAIL("", "TypeAliasTemplate is not exposed via LibClang", Abort);
1881 #endif
1882     QVERIFY(templateAlias->isTypeAlias());
1883     QVERIFY(templateAlias->abstractType());
1884     QCOMPARE(templateAlias->abstractType()->toString(), QStringLiteral("Alias"));
1885     QCOMPARE(templateAlias->uses().size(), 1);
1886     QCOMPARE(templateAlias->uses().first().size(), 1);
1887     QCOMPARE(templateAlias->uses().first().first(), RangeInRevision(0, 51, 0, 56));
1888 }
1889 
1890 void TestDUChain::testDeclarationsInsideMacroExpansion()
1891 {
1892     TestFile header(QStringLiteral("#define DECLARE(a) typedef struct a##__ {int var;} *a\nDECLARE(D);\n"), QStringLiteral("h"));
1893     TestFile file("#include \"" + header.url().str() + "\"\nint main(){\nD d; d->var;}\n", QStringLiteral("cpp"));
1894 
1895     file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST);
1896     QVERIFY(file.waitForParsed(5000));
1897 
1898     {
1899         DUChainReadLocker lock;
1900         QVERIFY(file.topContext());
1901     }
1902 
1903     file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST | TopDUContext::ForceUpdate);
1904     QVERIFY(file.waitForParsed(5000));
1905 
1906     DUChainReadLocker lock;
1907     QVERIFY(file.topContext());
1908     QCOMPARE(file.topContext()->localDeclarations().size(), 1);
1909 
1910     auto context = file.topContext()->childContexts().first()->childContexts().first();
1911     QVERIFY(context);
1912     QCOMPARE(context->localDeclarations().size(), 1);
1913     QCOMPARE(context->usesCount(), 3);
1914 
1915     QCOMPARE(context->uses()[0].m_range, RangeInRevision({2, 0}, {2, 1}));
1916     QCOMPARE(context->uses()[1].m_range, RangeInRevision({2, 5}, {2, 6}));
1917     QCOMPARE(context->uses()[2].m_range, RangeInRevision({2, 8}, {2, 11}));
1918 }
1919 
1920 // see also: https://bugs.kde.org/show_bug.cgi?id=368067
1921 void TestDUChain::testForwardTemplateTypeParameterContext()
1922 {
1923     TestFile file(QStringLiteral(R"(
1924         template<typename MatchingName> class Foo;
1925 
1926         class MatchingName { void bar(); };
1927         void MatchingName::bar() {  }
1928     )"), QStringLiteral("cpp"));
1929 
1930     file.parse();
1931     QVERIFY(file.waitForParsed(500));
1932     DUChainReadLocker lock;
1933     const auto top = file.topContext();
1934     QVERIFY(top);
1935     DUChainDumper dumper(DUChainDumper::Features(DUChainDumper::DumpContext | DUChainDumper::DumpProblems));
1936     dumper.dump(top);
1937 
1938     auto declarations = top->localDeclarations();
1939     QCOMPARE(declarations.size(), 2);
1940 }
1941 
1942 // see also: https://bugs.kde.org/show_bug.cgi?id=368460
1943 void TestDUChain::testTemplateFunctionParameterName()
1944 {
1945     TestFile file(QStringLiteral(R"(
1946         template<class T>
1947         void foo(int name);
1948 
1949         void bar(int name);
1950     )"), QStringLiteral("cpp"));
1951 
1952     file.parse();
1953     QVERIFY(file.waitForParsed(500));
1954     DUChainReadLocker lock;
1955     const auto top = file.topContext();
1956     QVERIFY(top);
1957     DUChainDumper dumper(DUChainDumper::Features(DUChainDumper::DumpContext | DUChainDumper::DumpProblems));
1958     dumper.dump(top);
1959 
1960     const auto declarations = top->localDeclarations();
1961     QCOMPARE(declarations.size(), 2);
1962 
1963     for (auto decl : declarations) {
1964         auto ctx = DUChainUtils::argumentContext(decl);
1965         QVERIFY(ctx);
1966         auto args = ctx->localDeclarations();
1967         if (decl == declarations.first())
1968             QEXPECT_FAIL("", "We get two declarations, for both template and args :(", Continue);
1969         QCOMPARE(args.size(), 1);
1970         if (decl == declarations.first())
1971             QEXPECT_FAIL("", "see above, this then triggers T T here", Continue);
1972         QCOMPARE(args.first()->toString(), QStringLiteral("int name"));
1973     }
1974 }
1975 
1976 static bool containsErrors(const QList<Problem::Ptr>& problems)
1977 {
1978     auto it = std::find_if(problems.begin(), problems.end(), [] (const Problem::Ptr& problem) {
1979         return problem->severity() == Problem::Error;
1980     });
1981     return it != problems.end();
1982 }
1983 
1984 static bool expectedXmmintrinErrors(const QList<Problem::Ptr>& problems)
1985 {
1986     for (const auto& problem : problems) {
1987         if (problem->severity() == Problem::Error && !problem->description().contains(QLatin1String("Cannot initialize a parameter of type"))) {
1988             return false;
1989         }
1990     }
1991     return true;
1992 }
1993 
1994 static void verifyNoErrors(TopDUContext* top, QSet<TopDUContext*>& checked)
1995 {
1996     const auto problems = top->problems();
1997     if (containsErrors(problems)) {
1998         qDebug() << top->url() << top->problems();
1999         if (top->url().str().endsWith(QLatin1String("xmmintrin.h")) && expectedXmmintrinErrors(problems)) {
2000             QEXPECT_FAIL("", "there are still some errors in xmmintrin.h b/c some clang provided intrinsincs are more strict than the GCC ones.", Continue);
2001             QVERIFY(false);
2002         } else {
2003             QFAIL("parse error detected");
2004         }
2005     }
2006     const auto imports = top->importedParentContexts();
2007     for (const auto& import : imports) {
2008         auto ctx = import.context(top);
2009         QVERIFY(ctx);
2010         auto importedTop = ctx->topContext();
2011         if (checked.contains(importedTop)) {
2012             continue;
2013         }
2014         checked.insert(importedTop);
2015         verifyNoErrors(importedTop, checked);
2016     }
2017 }
2018 
2019 void TestDUChain::testFriendDeclaration()
2020 {
2021     TestFile file(QStringLiteral(R"(
2022         struct FriendFoo
2023         {
2024             friend class FriendBar;
2025         };
2026 
2027         class FriendBar{};
2028 
2029         FriendBar friendBar;
2030     )"), QStringLiteral("cpp"));
2031     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
2032 
2033     QVERIFY(file.waitForParsed(1000));
2034     {
2035         DUChainReadLocker lock;
2036         QVERIFY(file.topContext());
2037         QCOMPARE(file.topContext()->localDeclarations().size(), 3);
2038 
2039         auto friendBar = file.topContext()->localDeclarations()[1];
2040         if (CINDEX_VERSION_MINOR < 37) {
2041             QEXPECT_FAIL("", "Your clang version is too old", Abort);
2042         }
2043         QCOMPARE(friendBar->uses().size(), 1);
2044         QCOMPARE(friendBar->uses().begin()->first(), RangeInRevision(3,25,3,34));
2045         QCOMPARE(friendBar->uses().begin()->last(), RangeInRevision(8,8,8,17));
2046     }
2047 }
2048 
2049 void TestDUChain::testVariadicTemplateArguments()
2050 {
2051     TestFile file(QStringLiteral(R"(
2052         template<typename T, typename... Targs>
2053         class VariadicTemplate {};
2054 
2055         VariadicTemplate<int, double, bool> variadic;
2056     )"), QStringLiteral("cpp"));
2057     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
2058 
2059     QVERIFY(file.waitForParsed(1000));
2060     {
2061         DUChainReadLocker lock;
2062         QVERIFY(file.topContext());
2063         QCOMPARE(file.topContext()->localDeclarations().size(), 2);
2064 
2065         auto decl = file.topContext()->localDeclarations()[1];
2066         QVERIFY(decl);
2067         if (CINDEX_VERSION_MINOR < 37) {
2068             QEXPECT_FAIL("", "Your clang version is too old", Abort);
2069         }
2070         QCOMPARE(decl->toString(), QStringLiteral("VariadicTemplate< int, double, bool > variadic"));
2071 
2072         QVERIFY(decl->abstractType());
2073         QCOMPARE(decl->abstractType()->toString(), QStringLiteral("VariadicTemplate< int, double, bool >"));
2074     }
2075 }
2076 
2077 void TestDUChain::testProblemRequestedHere()
2078 {
2079     auto headerCode = QStringLiteral(R"(
2080 #pragma once
2081 
2082 template <typename T>
2083 T AddObjects(const T& a, const T& b)
2084 {
2085   return a + b;
2086 }
2087     )");
2088     TestFile header(headerCode, QStringLiteral("h"));
2089 
2090     QString sourceCode = QStringLiteral(R"(
2091 #include "%1"
2092 struct A {};
2093 
2094 int main()
2095 {
2096     A a, b;
2097     AddObjects(a, b);
2098     return 0;
2099 }
2100     )").arg(header.url().str());
2101     TestFile impl(sourceCode, QStringLiteral("cpp"), &header);
2102     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses));
2103 
2104     DUChainReadLocker lock;
2105 
2106     auto top = impl.topContext();
2107     QVERIFY(top);
2108 
2109     auto* headerCtx = dynamic_cast<TopDUContext*>(top->importedParentContexts().first().context(top));
2110     QVERIFY(headerCtx);
2111     QCOMPARE(headerCtx->url(), header.url());
2112 
2113     QCOMPARE(headerCtx->problems().count(), 1);
2114     QCOMPARE(headerCtx->localDeclarations().count(), 1);
2115 
2116     // Verify that the problem is reported for the source file.
2117     QCOMPARE(top->problems().count(), 1);
2118     QCOMPARE(top->localDeclarations().count(), 2);
2119 }
2120 
2121 void TestDUChain::testProblemRequestedHereSameFile()
2122 {
2123     auto sourceCode = QStringLiteral(R"(
2124 struct A {};
2125 
2126 template <typename T>
2127 T AddObjects(const T& a, const T& b)
2128 {
2129   return a + b;
2130 }
2131 
2132 int main()
2133 {
2134     A a, b;
2135     AddObjects(a, b);
2136     return 0;
2137 }
2138     )");
2139     TestFile impl(sourceCode, QStringLiteral("cpp"));
2140     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses));
2141 
2142     DUChainReadLocker lock;
2143 
2144     auto top = impl.topContext();
2145     QVERIFY(top);
2146 
2147     QCOMPARE(top->problems().count(), 2);
2148 }
2149 
2150 void TestDUChain::testProblemRequestedHereChain()
2151 {
2152     auto headerCode = QStringLiteral(R"(
2153 #pragma once
2154 
2155 template <typename T>
2156 T AddObjects(const T& a, const T& b)
2157 {
2158   return a + b;
2159 }
2160     )");
2161     TestFile header(headerCode, QStringLiteral("h"));
2162 
2163     QString sourceCode = QStringLiteral(R"(
2164 #include "%1"
2165 struct A {};
2166 
2167 template <typename T>
2168 T AddObjects2(const T& a, const T& b)
2169 {
2170   return AddObjects(a, b);
2171 }
2172 
2173 int main()
2174 {
2175     A a, b;
2176     AddObjects2(a, b);
2177     return 0;
2178 }
2179     )").arg(header.url().str());
2180     TestFile impl(sourceCode, QStringLiteral("cpp"), &header);
2181     QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsAndUses));
2182 
2183     DUChainReadLocker lock;
2184 
2185     auto top = impl.topContext();
2186     QVERIFY(top);
2187 
2188     auto* headerCtx = dynamic_cast<TopDUContext*>(top->importedParentContexts().first().context(top));
2189     QVERIFY(headerCtx);
2190     QCOMPARE(headerCtx->url(), header.url());
2191 
2192     QCOMPARE(headerCtx->problems().count(), 1);
2193     QCOMPARE(headerCtx->localDeclarations().count(), 1);
2194 
2195     // Verify that the problem is reported for the source file.
2196     QCOMPARE(top->problems().count(), 2);
2197     QCOMPARE(top->localDeclarations().count(), 3);
2198 }
2199 
2200 void TestDUChain::testGccCompatibility_data()
2201 {
2202     QTest::addColumn<QString>("parserArguments");
2203     QTest::addColumn<QString>("code");
2204 
2205     QTest::newRow("x86intrin") << QString() << QStringLiteral(R"(
2206             #include <x86intrin.h>
2207 
2208             int main() { return 0; }
2209         )");
2210     QTest::newRow("sized-dealloc") << QStringLiteral("-Wall -std=c++14") << QStringLiteral(R"(
2211             #include <memory>
2212 
2213             int main() {
2214                 auto test = std::make_shared<uint32_t>();
2215                 return 0;
2216             }
2217         )");
2218 }
2219 
2220 void TestDUChain::testGccCompatibility()
2221 {
2222     QFETCH(QString, parserArguments);
2223     QFETCH(QString, code);
2224 
2225     // TODO: make it easier to change the compiler provider for testing purposes
2226     QTemporaryDir dir;
2227     auto project = new TestProject(Path(dir.path()), this);
2228     auto definesAndIncludesConfig = project->projectConfiguration()->group("CustomDefinesAndIncludes");
2229     auto pathConfig = definesAndIncludesConfig.group("ProjectPath0");
2230     pathConfig.writeEntry("Path", ".");
2231     if (!parserArguments.isEmpty()) {
2232         pathConfig.writeEntry("parserArguments", parserArguments);
2233     }
2234     pathConfig.group("Compiler").writeEntry("Name", "GCC");
2235     m_projectController->addProject(project);
2236 
2237     {
2238         // TODO: Also test in C mode. Currently it doesn't work (some intrinsics missing?)
2239         TestFile file(code, QStringLiteral("cpp"), project, dir.path());
2240 
2241         file.parse();
2242         QVERIFY(file.waitForParsed(50000));
2243 
2244         DUChainReadLocker lock;
2245         QSet<TopDUContext*> checked;
2246         verifyNoErrors(file.topContext(), checked);
2247     }
2248 
2249     m_projectController->closeAllProjects();
2250 }
2251 
2252 void TestDUChain::testLambda()
2253 {
2254     TestFile file(QStringLiteral("auto lambda = [](int p1, int p2, int p3) { int var1, var2; };"), QStringLiteral("cpp"));
2255     QVERIFY(file.parseAndWait());
2256     {
2257         DUChainReadLocker lock;
2258         QVERIFY(file.topContext());
2259         QCOMPARE(file.topContext()->localDeclarations().size(), 1);
2260         QCOMPARE(file.topContext()->childContexts().size(), 1);
2261 
2262         auto lambdaContext = file.topContext()->childContexts().first();
2263 
2264         QCOMPARE(lambdaContext->type(), DUContext::Function);
2265 
2266         QCOMPARE(lambdaContext->localDeclarations().size(), 3);
2267         QCOMPARE(lambdaContext->childContexts().size(), 1);
2268         QCOMPARE(lambdaContext->childContexts().first()->type(), DUContext::Other);
2269         QCOMPARE(lambdaContext->childContexts().first()->localDeclarations().size(), 2);
2270     }
2271 }
2272 
2273 void TestDUChain::testQtIntegration()
2274 {
2275     QTemporaryDir includeDir;
2276     {
2277         QDir dir(includeDir.path());
2278         dir.mkdir(QStringLiteral("QtCore"));
2279         // create the file but don't put anything in it
2280         QFile header(includeDir.path() + "/QtCore/qobjectdefs.h");
2281         QVERIFY(header.open(QIODevice::WriteOnly | QIODevice::Text));
2282     }
2283     QTemporaryDir dir;
2284     auto project = new TestProject(Path(dir.path()), this);
2285     m_provider->defines.clear();
2286     m_provider->includes = {Path(includeDir.path() + "/QtCore")};
2287 
2288     m_projectController->addProject(project);
2289 
2290     {
2291         TestFile file(QStringLiteral(R"(
2292             #define slots
2293             #define signals
2294             #define Q_SLOTS
2295             #define Q_SIGNALS
2296             #include <QtCore/qobjectdefs.h>
2297 
2298             struct MyObject {
2299             public:
2300               void other1();
2301             public slots:
2302               void slot1();
2303             signals:
2304               void signal1();
2305             private Q_SLOTS:
2306               void slot2();
2307             Q_SIGNALS:
2308               void signal2();
2309             public:
2310               void other2();
2311             };
2312         )"), QStringLiteral("cpp"), project, dir.path());
2313 
2314         file.parse();
2315         QVERIFY(file.waitForParsed(5000));
2316 
2317         DUChainReadLocker lock;
2318         auto top = file.topContext();
2319         QVERIFY(top);
2320         QVERIFY(top->problems().isEmpty());
2321         const auto methods = top->childContexts().last()->localDeclarations();
2322         QCOMPARE(methods.size(), 6);
2323         for (auto method : methods) {
2324             auto classFunction = dynamic_cast<ClassFunctionDeclaration*>(method);
2325             QVERIFY(classFunction);
2326             auto id = classFunction->identifier().toString();
2327             QCOMPARE(classFunction->isSignal(), id.startsWith(QLatin1String("signal")));
2328             QCOMPARE(classFunction->isSlot(), id.startsWith(QLatin1String("slot")));
2329         }
2330     }
2331 
2332     m_projectController->closeAllProjects();
2333 }
2334 
2335 void TestDUChain::testHasInclude()
2336 {
2337     TestFile header(QStringLiteral(R"(
2338         #pragma once
2339         #if __has_include_next(<atomic>)
2340         // good
2341         #else
2342         #error broken c++11 setup (__has_include_next)
2343         #endif
2344     )"), QStringLiteral("h"));
2345     // NOTE: header is _not_ explicitly being parsed, instead the impl job does that
2346 
2347     TestFile file(QStringLiteral(R"(
2348         #if __has_include(<atomic>)
2349         // good
2350         #else
2351         #error broken c++11 setup (__has_include)
2352         #endif
2353         #include "%1"
2354     )").arg(header.url().str()), QStringLiteral("cpp"));
2355     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
2356 
2357     QVERIFY(file.waitForParsed(1000));
2358     {
2359         DUChainDumper dumper{DUChainDumper::DumpProblems};
2360 
2361         DUChainReadLocker lock;
2362 
2363         QVERIFY(file.topContext());
2364         dumper.dump(file.topContext());
2365         QVERIFY(file.topContext()->problems().isEmpty());
2366 
2367         auto headerCtx = DUChain::self()->chainForDocument(header.url());
2368         QVERIFY(headerCtx);
2369         dumper.dump(headerCtx);
2370         QVERIFY(headerCtx->problems().count() <= 1);
2371         const auto& headerProblems = headerCtx->problems();
2372         for (const auto& problem : headerProblems) {
2373             // ignore the following error:  "#include_next with absolute path [-Winclude-next-absolute-path]" "" [ (2, 12)  ->  (2, 30) ]
2374             QVERIFY(problem->description().contains(QLatin1String("-Winclude-next-absolute-path")));
2375         }
2376     }
2377 }
2378 
2379 void TestDUChain::testSameFunctionDefinition()
2380 {
2381     QString source(QStringLiteral(R"(
2382         #include <stdio.h>
2383         #include <stdlib.h>
2384 
2385         void configure()
2386         {
2387             printf("do stuff\n");
2388         }
2389     )"));
2390 
2391     QTemporaryDir dir;
2392     auto project = new TestProject(Path(dir.path()), this);
2393     m_projectController->addProject(project);
2394 
2395     TestFile file1(source, QStringLiteral("c"), project);
2396     TestFile file2(source, QStringLiteral("c"), project);
2397     TestFile file3(source, QStringLiteral("c"), project);
2398 
2399     file1.parse(TopDUContext::AllDeclarationsContextsAndUses);
2400     file2.parse(TopDUContext::AllDeclarationsContextsAndUses);
2401     file3.parse(TopDUContext::AllDeclarationsContextsAndUses);
2402 
2403     QVERIFY(file1.waitForParsed(1000));
2404     QVERIFY(file2.waitForParsed(1000));
2405     QVERIFY(file3.waitForParsed(1000));
2406 
2407     auto checkFunctionDefinition = [] (TestFile & file) {
2408         DUChainReadLocker lock;
2409         QCOMPARE(file.topContext()->localDeclarations().count(), 1);
2410         auto configureFunc = file.topContext()->localDeclarations().first();
2411         QCOMPARE(FunctionDefinition::definition(configureFunc), configureFunc);
2412     };
2413 
2414     checkFunctionDefinition(file1);
2415     checkFunctionDefinition(file2);
2416     checkFunctionDefinition(file3);
2417 
2418     m_projectController->closeAllProjects();
2419 }
2420 
2421 void TestDUChain::testSizeAlignOf()
2422 {
2423     TestFile file(QStringLiteral(R"(
2424         template<typename T>
2425         struct Foo { T arg; };
2426 
2427         using Bar = Foo<int>;
2428         Bar asdf;
2429         Foo<double> blub;
2430         template<typename F>
2431         using Asdf = Foo<F>;
2432         Asdf<char> c;
2433 
2434         struct MyClass {
2435             int i;
2436             int j;
2437         };
2438         void *ptr = nullptr;
2439 
2440         enum E { A, B };
2441         E e = A;
2442         enum class E2 : int { A, B };
2443         E2 e2 = E2::A;
2444         enum class E3 : long { A, B };
2445         E3 e3 = E3::A;
2446 
2447         int i = 0;
2448         int& ref = i;
2449         struct ref_struct { int& ref; };
2450 
2451         int fun();
2452 
2453         namespace myns { struct bar { int foo; }; }
2454         myns::bar myns_use;
2455 
2456         struct MyClass c_struct;
2457     )"),
2458                   QStringLiteral("cpp"));
2459     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
2460     QVERIFY(file.waitForParsed(1000));
2461 
2462     DUChainReadLocker lock;
2463     const auto top = file.topContext();
2464     QVERIFY(top);
2465     QVERIFY(top->problems().isEmpty());
2466 
2467     const auto decs = top->localDeclarations();
2468     QCOMPARE(decs.size(), 21);
2469 
2470     auto check = [&](int i, const QString& id, qint64 sizeOf, qint64 alignOf) {
2471         QCOMPARE(decs[i]->identifier().toString(), id);
2472         const auto type = TypeUtils::unAliasedType(decs[i]->abstractType());
2473         QVERIFY(type);
2474         QCOMPARE(type->sizeOf(), sizeOf);
2475         QCOMPARE(type->alignOf(), alignOf);
2476     };
2477     check(0, "Foo< T >", -1, -1);
2478     check(1, "Bar", 4, 4);
2479     check(2, "asdf", 4, 4);
2480     check(3, "blub", 8, 8);
2481     check(4, "Asdf", -1, -1);
2482     check(5, "c", 1, 1);
2483     check(6, "MyClass", 8, 4);
2484     check(7, "ptr", 8, 8);
2485     check(8, "E", 4, 4);
2486     check(9, "e", 4, 4);
2487     check(10, "E2", 4, 4);
2488     check(11, "e2", 4, 4);
2489     check(12, "E3", 8, 8);
2490     check(13, "e3", 8, 8);
2491     check(14, "i", 4, 4);
2492     // unexpected, but apparently correct
2493     // https://stackoverflow.com/questions/26631169/why-does-sizeof-a-reference-type-give-you-the-sizeof-the-type
2494     check(15, "ref", 4, 4);
2495     check(16, "ref_struct", 8, 8);
2496     check(17, "fun", -1, -1);
2497     check(19, "myns_use", 4, 4);
2498     check(20, "c_struct", 8, 4);
2499 }
2500 
2501 void TestDUChain::testSizeAlignOfUpdate()
2502 {
2503     TestFile file(QStringLiteral(R"(
2504         struct foo { int i; };
2505     )"),
2506                   QStringLiteral("cpp"));
2507     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
2508     QVERIFY(file.waitForParsed(1000));
2509 
2510     {
2511         DUChainReadLocker lock;
2512         const auto top = file.topContext();
2513         QVERIFY(top);
2514         QVERIFY(top->problems().isEmpty());
2515         QCOMPARE(top->localDeclarations().size(), 1);
2516         const auto type = top->localDeclarations().constFirst()->abstractType();
2517         QCOMPARE(type->sizeOf(), 4);
2518         QCOMPARE(type->alignOf(), 4);
2519     }
2520 
2521     file.setFileContents(QStringLiteral(R"(
2522         struct foo { char i[124]; };
2523     )"));
2524     file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdate);
2525     QVERIFY(file.waitForParsed(1000));
2526 
2527     {
2528         DUChainReadLocker lock;
2529         const auto top = file.topContext();
2530         QVERIFY(top);
2531         QVERIFY(top->problems().isEmpty());
2532         QCOMPARE(top->localDeclarations().size(), 1);
2533         const auto type = top->localDeclarations().constFirst()->abstractType();
2534         QCOMPARE(type->sizeOf(), 124);
2535         QCOMPARE(type->alignOf(), 1);
2536     }
2537 }
2538 
2539 void TestDUChain::testBitWidth()
2540 {
2541 #if CINDEX_VERSION_MINOR >= 16
2542     TestFile file(QStringLiteral(R"(
2543         #include <climits>
2544 
2545         struct foo {
2546             int a;
2547             unsigned int b:1;
2548             unsigned char c:7;
2549             int d:32;
2550             int e[2];
2551             unsigned       length    : (sizeof(int)*CHAR_BIT - 1);
2552 
2553             // error case
2554             int f:0;
2555             int g:-1;
2556             int h:33;
2557         };)"), QStringLiteral("cpp"));
2558 
2559     QVERIFY(file.parseAndWait());
2560     {
2561         DUChainReadLocker lock;
2562         QVERIFY(file.topContext());
2563         QCOMPARE(file.topContext()->localDeclarations().size(), 1);
2564         QCOMPARE(file.topContext()->childContexts().size(), 1);
2565 
2566         auto fooContext = file.topContext()->childContexts().first();
2567         QVERIFY(fooContext);
2568         QCOMPARE(fooContext->type(), DUContext::Class);
2569         QCOMPARE(fooContext->localDeclarations().size(), 9);
2570         QCOMPARE(fooContext->childContexts().size(), 0);
2571 
2572         auto varA = dynamic_cast<ClassMemberDeclaration *>(fooContext->localDeclarations().at(0));
2573         QVERIFY(varA);
2574         QCOMPARE(varA->bitWidth(), ClassMemberDeclaration::NotABitField);
2575         auto varB = dynamic_cast<ClassMemberDeclaration *>(fooContext->localDeclarations().at(1));
2576         QVERIFY(varB);
2577         QCOMPARE(varB->bitWidth(), 1);
2578         auto varC = dynamic_cast<ClassMemberDeclaration *>(fooContext->localDeclarations().at(2));
2579         QVERIFY(varC);
2580         QCOMPARE(varC->bitWidth(), 7);
2581         auto varD = dynamic_cast<ClassMemberDeclaration *>(fooContext->localDeclarations().at(3));
2582         QVERIFY(varD);
2583         QCOMPARE(varD->bitWidth(), 32);
2584         auto varE = dynamic_cast<ClassMemberDeclaration *>(fooContext->localDeclarations().at(4));
2585         QVERIFY(varE);
2586         QCOMPARE(varE->bitWidth(), ClassMemberDeclaration::NotABitField);
2587         const auto varLength = dynamic_cast<const ClassMemberDeclaration*>(fooContext->localDeclarations().at(5));
2588         QVERIFY(varLength);
2589         QCOMPARE(varLength->bitWidth(), sizeof(int) * CHAR_BIT - 1);
2590 
2591         // error case
2592         auto top = file.topContext();
2593         QVERIFY(top);
2594         QCOMPARE(top->problems().count(), 3);
2595 
2596         // error: named bit-field 'f' has zero width
2597         auto varF = dynamic_cast<ClassMemberDeclaration*>(fooContext->localDeclarations().at(6));
2598         QVERIFY(varF);
2599         QCOMPARE(varF->bitWidth(), ClassMemberDeclaration::NotABitField);
2600         // error: bit-field 'g' has negative width (-1)
2601         auto varG = dynamic_cast<ClassMemberDeclaration*>(fooContext->localDeclarations().at(7));
2602         QVERIFY(varG);
2603         QCOMPARE(varG->bitWidth(), ClassMemberDeclaration::NotABitField);
2604         // warning: width of bit-field 'h' (33 bits) exceeds the width of its type; value will be truncated to 32 bits
2605         auto varH = dynamic_cast<ClassMemberDeclaration*>(fooContext->localDeclarations().at(8));
2606         QVERIFY(varH);
2607         QCOMPARE(varH->bitWidth(), 33);
2608     }
2609 #endif
2610 }
2611 
2612 void TestDUChain::testValueDependentBitWidth()
2613 {
2614     if (QVersionNumber::fromString(ClangHelpers::clangVersion()) < QVersionNumber(17, 0, 0)) {
2615         QSKIP("Parsing value-dependent bit-fields may cause an assertion failure or a crash in libclang version < 17");
2616     }
2617 
2618     TestFile file(QStringLiteral(R"(
2619         #include <climits>
2620 
2621         template<class T, unsigned I>
2622         struct Str {
2623             typedef typename T::size_type size_type;
2624 
2625             int w : sizeof(double);
2626             int x : sizeof(T);
2627             short y : I + 1;
2628             unsigned       length1   : (sizeof(size_type)*CHAR_BIT - 1);
2629             size_type      length2   : (sizeof(size_type)*CHAR_BIT - 1);
2630             size_type z = 5;
2631         };
2632     )"),
2633                   QStringLiteral("cpp"));
2634 
2635     QVERIFY(file.parseAndWait());
2636     {
2637         DUChainReadLocker lock;
2638         QVERIFY(file.topContext());
2639         QCOMPARE(file.topContext()->localDeclarations().size(), 1);
2640         QCOMPARE(file.topContext()->childContexts().size(), 1);
2641 
2642         const auto strContext = file.topContext()->childContexts().constFirst();
2643         QVERIFY(strContext);
2644         QCOMPARE(strContext->type(), DUContext::Class);
2645         QCOMPARE(strContext->childContexts().size(), 0);
2646 
2647         const auto strDeclarations = strContext->localDeclarations();
2648         QCOMPARE(strDeclarations.size(), 9);
2649 
2650         const auto varW = dynamic_cast<const ClassMemberDeclaration*>(strDeclarations.at(3));
2651         QVERIFY(varW);
2652         QCOMPARE(varW->bitWidth(), sizeof(double));
2653 
2654         for (int i = 4; i < strDeclarations.size() - 1; ++i) {
2655             const auto member = dynamic_cast<const ClassMemberDeclaration*>(strDeclarations.at(i));
2656             QVERIFY(member);
2657             QCOMPARE(member->bitWidth(), ClassMemberDeclaration::ValueDependentBitWidth);
2658         }
2659 
2660         const auto varZ = dynamic_cast<const ClassMemberDeclaration*>(strDeclarations.constLast());
2661         QVERIFY(varZ);
2662         QCOMPARE(varZ->bitWidth(), ClassMemberDeclaration::NotABitField);
2663     }
2664 }
2665 
2666 void TestDUChain::testBitWidthUpdate()
2667 {
2668 #if CINDEX_VERSION_MINOR >= 16
2669     TestFile file(QStringLiteral(R"(
2670         struct foo { int i:7; };
2671     )"),
2672                   QStringLiteral("cpp"));
2673     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
2674     QVERIFY(file.waitForParsed(1000));
2675 
2676     {
2677         DUChainReadLocker lock;
2678         const auto top = file.topContext();
2679         QVERIFY(top);
2680         QVERIFY(top->problems().isEmpty());
2681         QCOMPARE(top->localDeclarations().size(), 1);
2682         QCOMPARE(top->childContexts().size(), 1);
2683 
2684         const auto fooContext = top->childContexts().first();
2685         QVERIFY(fooContext);
2686         QCOMPARE(fooContext->type(), DUContext::Class);
2687         QCOMPARE(fooContext->localDeclarations().size(), 1);
2688         QCOMPARE(fooContext->childContexts().size(), 0);
2689 
2690         const auto varI = dynamic_cast<ClassMemberDeclaration *>(fooContext->localDeclarations().at(0));
2691         QCOMPARE(varI->bitWidth(), 7);
2692     }
2693 
2694     file.setFileContents(QStringLiteral(R"(
2695         struct foo { int i; };
2696     )"));
2697     file.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdate);
2698     QVERIFY(file.waitForParsed(1000));
2699 
2700     {
2701         DUChainReadLocker lock;
2702         const auto top = file.topContext();
2703         QVERIFY(top);
2704         QVERIFY(top->problems().isEmpty());
2705         QCOMPARE(top->localDeclarations().size(), 1);
2706         QCOMPARE(top->childContexts().size(), 1);
2707 
2708         const auto fooContext = top->childContexts().first();
2709         QVERIFY(fooContext);
2710         QCOMPARE(fooContext->type(), DUContext::Class);
2711         QCOMPARE(fooContext->localDeclarations().size(), 1);
2712         QCOMPARE(fooContext->childContexts().size(), 0);
2713 
2714         const auto varI = dynamic_cast<ClassMemberDeclaration *>(fooContext->localDeclarations().at(0));
2715         QCOMPARE(varI->bitWidth(), ClassMemberDeclaration::NotABitField);
2716     }
2717 #endif
2718 }
2719 
2720 #include "moc_test_duchain.cpp"