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"