Warning, file /kdevelop/kdev-php/duchain/tests/duchain_multiplefiles.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2010 Niko Sams <niko.sams@gmail.com>
0003     SPDX-FileCopyrightText: 2011 Milian Wolff <mail@milianw.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "duchain_multiplefiles.h"
0009 
0010 #include <QtTest>
0011 
0012 #include <tests/testcore.h>
0013 #include <interfaces/ilanguagecontroller.h>
0014 #include <interfaces/icompletionsettings.h>
0015 #include <language/backgroundparser/backgroundparser.h>
0016 #include <tests/testproject.h>
0017 #include <tests/testfile.h>
0018 #include <language/duchain/declaration.h>
0019 #include <language/duchain/problem.h>
0020 #include <language/duchain/types/integraltype.h>
0021 
0022 QTEST_MAIN(Php::TestDUChainMultipleFiles)
0023 
0024 using namespace KDevelop;
0025 using namespace Php;
0026 
0027 void TestDUChainMultipleFiles::initTestCase()
0028 {
0029     DUChainTestBase::initTestCase();
0030     TestCore* core = dynamic_cast<TestCore*>(ICore::self());
0031     Q_ASSERT(core);
0032     m_projectController = new TestProjectController(core);
0033     core->setProjectController(m_projectController);
0034 }
0035 
0036 void TestDUChainMultipleFiles::testImportsGlobalFunction()
0037 {
0038     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0039 
0040     TestProject* project = new TestProject;
0041     m_projectController->closeAllProjects();
0042     m_projectController->addProject(project);
0043 
0044     TestFile f1(QStringLiteral("<? function foo() {}"), QStringLiteral("php"), project);
0045     f1.parse(features);
0046     QVERIFY(f1.waitForParsed());
0047 
0048     TestFile f2(QStringLiteral("<? foo();"), QStringLiteral("php"), project);
0049     f2.parse(features);
0050     QVERIFY(f2.waitForParsed());
0051 
0052     DUChainWriteLocker lock(DUChain::lock());
0053     QVERIFY(f1.topContext());
0054     QVERIFY(f2.topContext());
0055     QVERIFY(f2.topContext()->imports(f1.topContext(), CursorInRevision(0, 0)));
0056 }
0057 
0058 void TestDUChainMultipleFiles::testImportsBaseClassNotYetParsed()
0059 {
0060     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0061 
0062     TestProject* project = new TestProject;
0063     m_projectController->closeAllProjects();
0064     m_projectController->addProject(project);
0065 
0066     TestFile f2(QStringLiteral("<? class B extends A {}"), QStringLiteral("php"), project);
0067     f2.parse(features);
0068 
0069     TestFile f1(QStringLiteral("<? class A {}"), QStringLiteral("php"), project);
0070     f1.parseAndWait(features, 100, 2000); //low priority, to make sure f2 is parsed first
0071 
0072     QVERIFY(f1.isReady());
0073 
0074     DUChainWriteLocker lock(DUChain::lock());
0075     QVERIFY(f2.topContext()->imports(f1.topContext(), CursorInRevision(0, 0)));
0076     QVERIFY(ICore::self()->languageController()->backgroundParser()->queuedCount() == 0);
0077 }
0078 
0079 void TestDUChainMultipleFiles::testNonExistingBaseClass()
0080 {
0081     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0082 
0083     TestProject* project = new TestProject;
0084     m_projectController->closeAllProjects();
0085     m_projectController->addProject(project);
0086 
0087     TestFile f1(QStringLiteral("<? class B extends A {}"), QStringLiteral("php"), project);
0088     f1.parse(features);
0089     QVERIFY(f1.waitForParsed());
0090 
0091     //there must not be a re-enqueued parsejob
0092     QVERIFY(ICore::self()->languageController()->backgroundParser()->queuedCount() == 0);
0093 }
0094 
0095 void TestDUChainMultipleFiles::testImportsGlobalFunctionNotYetParsed()
0096 {
0097     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0098 
0099     TestProject* project = new TestProject;
0100     m_projectController->closeAllProjects();
0101     m_projectController->addProject(project);
0102 
0103     TestFile f2(QStringLiteral("<? foo2();"), QStringLiteral("php"), project);
0104     f2.parse(features);
0105 
0106     TestFile f1(QStringLiteral("<? function foo2() {}"), QStringLiteral("php"), project);
0107     f1.parseAndWait(features, 100, 2000); //low priority, to make sure f2 is parsed first
0108 
0109     QVERIFY(f1.isReady());
0110 
0111     DUChainWriteLocker lock(DUChain::lock());
0112     QVERIFY(f2.topContext()->imports(f1.topContext(), CursorInRevision(0, 0)));
0113 }
0114 
0115 void TestDUChainMultipleFiles::testNonExistingGlobalFunction()
0116 {
0117     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0118 
0119     TestProject* project = new TestProject;
0120     m_projectController->closeAllProjects();
0121     m_projectController->addProject(project);
0122 
0123     TestFile f2(QStringLiteral("<? foo3();"), QStringLiteral("php"), project);
0124     f2.parse(features);
0125 
0126     QVERIFY(f2.waitForParsed());
0127      //there must not be a re-enqueued parsejob
0128     QVERIFY(ICore::self()->languageController()->backgroundParser()->queuedCount() == 0);
0129 }
0130 
0131 void TestDUChainMultipleFiles::testImportsStaticFunctionNotYetParsed()
0132 {
0133     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0134 
0135     TestProject* project = new TestProject;
0136     m_projectController->closeAllProjects();
0137     m_projectController->addProject(project);
0138 
0139     TestFile f2(QStringLiteral("<? C::foo();"), QStringLiteral("php"), project);
0140     f2.parse(features);
0141 
0142     TestFile f1(QStringLiteral("<? class C { public static function foo() {} }"), QStringLiteral("php"), project);
0143     f1.parseAndWait(features, 100, 2000); //low priority, to make sure f2 is parsed first
0144 
0145     QVERIFY(f1.isReady());
0146 
0147     DUChainWriteLocker lock(DUChain::lock());
0148     QVERIFY(f2.topContext()->imports(f1.topContext(), CursorInRevision(0, 0)));
0149 }
0150 
0151 void TestDUChainMultipleFiles::testNonExistingStaticFunction()
0152 {
0153     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0154 
0155     TestProject* project = new TestProject;
0156     m_projectController->closeAllProjects();
0157     m_projectController->addProject(project);
0158 
0159     TestFile f2(QStringLiteral("<? D::foo();"), QStringLiteral("php"), project);
0160     f2.parse(features);
0161 
0162     QVERIFY(f2.waitForParsed());
0163      //there must not be a re-enqueued parsejob
0164     QVERIFY(ICore::self()->languageController()->backgroundParser()->queuedCount() == 0);
0165 }
0166 
0167 void TestDUChainMultipleFiles::testForeachImportedIdentifier()
0168 {
0169     // see https://bugs.kde.org/show_bug.cgi?id=269369
0170 
0171     TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;
0172 
0173     TestProject* project = new TestProject;
0174     m_projectController->closeAllProjects();
0175     m_projectController->addProject(project);
0176 
0177     // build dependency
0178     TestFile f1(QStringLiteral("<? class SomeIterator implements Countable, Iterator { }"), QStringLiteral("php"), project);
0179     f1.parse(features);
0180     QVERIFY(f1.waitForParsed());
0181 
0182     TestFile f2(QStringLiteral("<?\n"
0183                 "class A {\n"
0184                 "  public function foo() { $i = $this->bar(); foreach($i as $a => $b) {} } \n"
0185                 "  public function bar() { $a = new SomeIterator(); return $a; }\n"
0186                 " }\n"), QStringLiteral("php"), project);
0187 
0188     for(int i = 0; i < 2; ++i) {
0189         if (i > 0) {
0190             features |= TopDUContext::ForceUpdate;
0191         }
0192         f2.parse(features);
0193         QVERIFY(f2.waitForParsed());
0194         QTest::qWait(100);
0195 
0196         DUChainWriteLocker lock(DUChain::lock());
0197         QCOMPARE(f2.topContext()->childContexts().size(), 1);
0198         DUContext* ACtx = f2.topContext()->childContexts().first();
0199         QVERIFY(ACtx);
0200         QCOMPARE(ACtx->childContexts().size(), 4);
0201         Declaration* iDec = ACtx->childContexts().at(1)->localDeclarations().first();
0202         QVERIFY(iDec);
0203         Declaration* SomeIteratorDec = f1.topContext()->localDeclarations().first();
0204         QVERIFY(SomeIteratorDec);
0205         if (i == 0) {
0206             QEXPECT_FAIL("", "needs a full two-pass (i.e. get rid of PreDeclarationBuilder)", Continue);
0207         }
0208         QVERIFY(iDec->abstractType()->equals(SomeIteratorDec->abstractType().constData()));
0209         QVERIFY(f2.topContext()->imports(f1.topContext(), CursorInRevision(0, 0)));
0210     }
0211 }
0212 
0213 void TestDUChainMultipleFiles::testUpdateForeach()
0214 {
0215     TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses;
0216 
0217     TestProject* project = new TestProject;
0218     m_projectController->closeAllProjects();
0219     m_projectController->addProject(project);
0220 
0221     TestFile f(QStringLiteral("<?\n$k = null;\nforeach(array() as $i => $k) {}\n"), QStringLiteral("php"), project);
0222 
0223     f.parse(features);
0224     QVERIFY(f.waitForParsed());
0225     QVERIFY(f.topContext());
0226 
0227     {
0228         DUChainWriteLocker lock;
0229         QVERIFY(f.topContext()->problems().isEmpty());
0230         QCOMPARE(f.topContext()->findDeclarations(Identifier(u"k")).count(), 1);
0231         Declaration* kDec = f.topContext()->findDeclarations(Identifier(QStringLiteral("k"))).first();
0232         QCOMPARE(kDec->rangeInCurrentRevision().start().line(), 1);
0233         QCOMPARE(kDec->rangeInCurrentRevision().start().column(), 0);
0234         QCOMPARE(kDec->uses().count(), 1);
0235         QCOMPARE(kDec->uses().begin()->count(), 1);
0236         QCOMPARE(kDec->uses().begin()->begin()->start.line, 2);
0237     }
0238 
0239     // delete $k = null; line
0240     f.setFileContents(QStringLiteral("<?\nforeach(array() as $i => $k) {}\n"));
0241     f.parse(features | TopDUContext::ForceUpdate);
0242     QVERIFY(f.waitForParsed());
0243     QVERIFY(f.topContext());
0244 
0245     {
0246         DUChainWriteLocker lock;
0247         QVERIFY(f.topContext()->problems().isEmpty());
0248         QCOMPARE(f.topContext()->findDeclarations(Identifier(u"k")).count(), 1);
0249         Declaration* kDec = f.topContext()->findDeclarations(Identifier(QStringLiteral("k"))).first();
0250         QCOMPARE(kDec->rangeInCurrentRevision().start().line(), 1);
0251         QCOMPARE(kDec->rangeInCurrentRevision().start().column(), 25);
0252         QCOMPARE(kDec->uses().count(), 0);
0253     }
0254 }
0255 
0256 void TestDUChainMultipleFiles::testTodoExtractorReparse()
0257 {
0258     TestFile file(QStringLiteral("<?php\n$foo = new bar();\n// TODO\n$foo->baz();"), QStringLiteral("php"));
0259 
0260     QVERIFY(KDevelop::ICore::self()->languageController()->completionSettings()->todoMarkerWords().contains("TODO"));
0261 
0262     TopDUContext::Features features{TopDUContext::AllDeclarationsContextsAndUses};
0263 
0264     for (int i = 0; i < 2; ++i) {
0265         if (i == 1) {
0266             file.setFileContents(QStringLiteral("<?php\n$foo = new bar();\n// TODO\n$foo->asdf();"));
0267             features |= TopDUContext::ForceUpdate;
0268         }
0269 
0270         file.parse(features);
0271         QVERIFY(file.waitForParsed());
0272 
0273         DUChainReadLocker lock;
0274         auto top = file.topContext();
0275         QVERIFY(top);
0276         QCOMPARE(top->problems().size(), 1);
0277         QCOMPARE(top->problems().at(0)->description(), QString("TODO"));
0278         QCOMPARE(top->problems().at(0)->range(), RangeInRevision(2, 3, 2, 7));
0279     }
0280 }
0281 
0282 void TestDUChainMultipleFiles::testIteratorForeachReparse() {
0283     TestFile file(QStringLiteral("<?php\n/*\n\n/*\n*/\nforeach (new A() as $a) {}\nclass A implements Iterator {\npublic function current() { return 0; }\n}"), QStringLiteral("php"));
0284 
0285     TopDUContext::Features features{TopDUContext::AllDeclarationsAndContexts};
0286 
0287     for (int i = 0; i < 2; ++i) {
0288         if (i == 1) {
0289             file.setFileContents(QStringLiteral("<?php\n/*\n*/\n\n/*\n*/\nforeach (new A() as $a) {}\nclass A implements Iterator {\npublic function current() { return 0; }\n}"));
0290             features |= TopDUContext::ForceUpdate;
0291         }
0292 
0293         file.parse(features);
0294         QVERIFY(file.waitForParsed());
0295 
0296         DUChainReadLocker lock;
0297         auto top = file.topContext();
0298         QVERIFY(top);
0299         QVERIFY(top->localDeclarations().size() == 2);
0300         QCOMPARE(top->localDeclarations().at(0)->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0301 
0302         IntegralType::Ptr type = top->localDeclarations().at(0)->type<IntegralType>();
0303         QVERIFY(type);
0304         //Should actually parse as an TypeInt, but this does not work.
0305         QVERIFY(type->dataType() == IntegralType::TypeMixed);
0306     }
0307 }
0308 
0309 void TestDUChainMultipleFiles::testNamespacedIdentifierInPST() {
0310     constexpr TopDUContext::Features features{TopDUContext::AllDeclarationsAndContexts};
0311 
0312     TestProject* project = new TestProject;
0313     m_projectController->closeAllProjects();
0314     m_projectController->addProject(project);
0315 
0316     TestFile f1(QStringLiteral("<?\n"
0317                 "namespace Test;\n"
0318                 "class A {}\n"), QStringLiteral("php"), project);
0319     f1.parse(features);
0320     QVERIFY(f1.waitForParsed());
0321 
0322     TestFile f2(QStringLiteral("<?\n"
0323                 "namespace Test2;\n"
0324                 "class B {\n"
0325                 "public $class_a;\n"
0326                 "public function __construct() { $this->class_a = new \\Test\\A(); }}"), QStringLiteral("php"), project);
0327     f2.parse(features);
0328     QVERIFY(f2.waitForParsed());
0329 
0330     TestFile f3(QStringLiteral("<?\n"
0331                 "namespace Test2;\n"
0332                 "class C {\n"
0333                 "public $class_a;\n"
0334                 "public function __construct() { $this->class_a = new Test\\A(); }}"), QStringLiteral("php"), project);
0335     f3.parse(features);
0336     QVERIFY(f3.waitForParsed());
0337 
0338     DUChainWriteLocker lock(DUChain::lock());
0339     QVERIFY(f1.topContext());
0340     QVERIFY(f2.topContext());
0341     QVERIFY(f2.topContext()->imports(f1.topContext(), CursorInRevision(0, 0)));
0342     QVERIFY(f3.topContext());
0343     QVERIFY(!f3.topContext()->imports(f1.topContext(), CursorInRevision(0, 0)));
0344 }
0345 
0346 #include "moc_duchain_multiplefiles.cpp"