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"