Warning, file /kdevelop/kdev-python/duchain/tests/pyduchaintest.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 Miquel Canes Gonzalez <miquelcanes@gmail.com> 0003 SPDX-FileCopyrightText: 2012 Sven Brauch <svenbrauch@googlemail.com> 0004 0005 SPDX-License-Identifier: MIT 0006 */ 0007 0008 #include <QDebug> 0009 #include "duchaindebug.h" 0010 0011 #include "pyduchaintest.h" 0012 0013 #include <stdlib.h> 0014 0015 #include <language/duchain/topducontext.h> 0016 #include <language/codegen/coderepresentation.h> 0017 #include <tests/autotestshell.h> 0018 #include <tests/testcore.h> 0019 #include <language/duchain/duchain.h> 0020 #include <QtTest> 0021 #include <QApplication> 0022 #include <language/duchain/types/functiontype.h> 0023 #include <language/duchain/types/containertypes.h> 0024 #include <language/duchain/aliasdeclaration.h> 0025 #include <language/backgroundparser/backgroundparser.h> 0026 #include <language/interfaces/iastcontainer.h> 0027 #include <interfaces/ilanguagecontroller.h> 0028 #include <tests/testfile.h> 0029 0030 #include <KTextEditor/Range> 0031 0032 #include "parsesession.h" 0033 #include "pythoneditorintegrator.h" 0034 #include "declarationbuilder.h" 0035 #include "usebuilder.h" 0036 #include "astdefaultvisitor.h" 0037 #include "expressionvisitor.h" 0038 #include "contextbuilder.h" 0039 #include "astbuilder.h" 0040 0041 #include "duchain/helpers.h" 0042 0043 #include "kdevpythonversion.h" 0044 0045 QTEST_MAIN(PyDUChainTest) 0046 0047 using namespace KDevelop; 0048 using namespace Python; 0049 0050 0051 PyDUChainTest::PyDUChainTest(QObject* parent): QObject(parent) 0052 { 0053 assetsDir = QDir(DUCHAIN_PY_DATA_DIR); 0054 if (!assetsDir.cd("data")) { 0055 qFatal("Failed find data directory for test files. Aborting"); 0056 } 0057 0058 testDir = QDir(testDirOwner.path()); 0059 0060 qputenv("PYTHONPATH", assetsDir.absolutePath().toUtf8()); 0061 0062 initShell(); 0063 } 0064 0065 QList<QString> PyDUChainTest::FindPyFiles(QDir& rootDir) 0066 { 0067 QList<QString> foundfiles; 0068 rootDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDot | QDir::NoDotDot); 0069 rootDir.setNameFilters(QStringList() << "*.py"); // We only want .py files 0070 0071 QDirIterator it(rootDir, QDirIterator::Subdirectories); 0072 while(it.hasNext()) { 0073 foundfiles.append(it.next()); 0074 } 0075 return foundfiles; 0076 } 0077 0078 void PyDUChainTest::init() 0079 { 0080 QString currentTest = QString(QTest::currentTestFunction()); 0081 if (lastTest == currentTest) { 0082 qCDebug(KDEV_PYTHON_DUCHAIN) << "Already prepared assets for " << currentTest << ", skipping"; 0083 return; 0084 } else { 0085 lastTest = currentTest; 0086 } 0087 qCDebug(KDEV_PYTHON_DUCHAIN) << "Preparing assets for test " << currentTest; 0088 0089 QDir assetModuleDir = QDir(assetsDir.absolutePath()); 0090 0091 if (!assetModuleDir.cd(currentTest)) { 0092 qCDebug(KDEV_PYTHON_DUCHAIN) << "Asset directory " << currentTest 0093 << " does not exist under " << assetModuleDir.absolutePath() << ". Skipping it."; 0094 return; 0095 } 0096 0097 qCDebug(KDEV_PYTHON_DUCHAIN) << "Searching for python files in " << assetModuleDir.absolutePath(); 0098 0099 QList<QString> foundfiles = FindPyFiles(assetModuleDir); 0100 0101 QString correctionFileDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kdevpythonsupport/correction_files", QStandardPaths::LocateDirectory); 0102 auto correctionFileUrl = QUrl(QDir::cleanPath(correctionFileDir + "/testCorrectionFiles/example.py")); 0103 foundfiles.prepend(correctionFileUrl.path()); 0104 0105 for ( int i = 0; i < 2; i++ ) { 0106 // Parse each file twice, to ensure no parsing-order related bugs appear. 0107 // Such bugs will need separate unit tests and should not influence these. 0108 foreach(const QString filename, foundfiles) { 0109 qCDebug(KDEV_PYTHON_DUCHAIN) << "Parsing asset: " << filename; 0110 DUChain::self()->updateContextForUrl(IndexedString(filename), KDevelop::TopDUContext::AllDeclarationsContextsAndUses); 0111 ICore::self()->languageController()->backgroundParser()->parseDocuments(); 0112 } 0113 foreach(const QString filename, foundfiles) { 0114 DUChain::self()->waitForUpdate(IndexedString(filename), KDevelop::TopDUContext::AllDeclarationsContextsAndUses); 0115 } 0116 while ( ICore::self()->languageController()->backgroundParser()->queuedCount() > 0 ) { 0117 // make sure to wait for all parsejobs to finish 0118 QTest::qWait(10); 0119 } 0120 } 0121 } 0122 0123 0124 void PyDUChainTest::initShell() 0125 { 0126 AutoTestShell::init(); 0127 TestCore* core = new TestCore(); 0128 core->initialize(KDevelop::Core::NoUi); 0129 0130 auto doc_url = QStandardPaths::locate(QStandardPaths::GenericDataLocation, 0131 "kdevpythonsupport/documentation_files/builtindocumentation.py"); 0132 0133 qCDebug(KDEV_PYTHON_DUCHAIN) << doc_url; 0134 0135 DUChain::self()->updateContextForUrl(IndexedString(doc_url), KDevelop::TopDUContext::AllDeclarationsContextsAndUses); 0136 ICore::self()->languageController()->backgroundParser()->parseDocuments(); 0137 DUChain::self()->waitForUpdate(IndexedString(doc_url), KDevelop::TopDUContext::AllDeclarationsContextsAndUses); 0138 0139 DUChain::self()->disablePersistentStorage(); 0140 KDevelop::CodeRepresentation::setDiskChangesForbidden(true); 0141 } 0142 0143 ReferencedTopDUContext PyDUChainTest::parse(const QString& code) 0144 { 0145 TestFile* testfile = new TestFile(code + "\n", "py", nullptr, testDir.absolutePath().append("/")); 0146 createdFiles << testfile; 0147 0148 testfile->parse(TopDUContext::ForceUpdate | TopDUContext::AST); 0149 testfile->waitForParsed(2000); 0150 0151 if ( testfile->isReady() ) { 0152 Q_ASSERT(testfile->topContext()); 0153 m_ast = static_cast<Python::ParseSession*>(testfile->topContext()->ast().data())->ast; 0154 return testfile->topContext(); 0155 } 0156 else Q_ASSERT(false && "Timed out waiting for parser results, aborting all tests"); 0157 return nullptr; 0158 } 0159 0160 PyDUChainTest::~PyDUChainTest() 0161 { 0162 foreach ( TestFile* f, createdFiles ) { 0163 delete f; 0164 } 0165 testDir.rmdir(testDir.absolutePath()); 0166 } 0167 0168 void PyDUChainTest::testMultiFromImport() 0169 { 0170 QFETCH(QString, code); 0171 ReferencedTopDUContext ctx = parse(code); 0172 QVERIFY(ctx); 0173 DUChainReadLocker lock; 0174 QList<Declaration*> a = ctx->findDeclarations(QualifiedIdentifier("a")); 0175 QList<Declaration*> b = ctx->findDeclarations(QualifiedIdentifier("b")); 0176 QVERIFY(! a.isEmpty()); 0177 QVERIFY(! b.isEmpty()); 0178 QVERIFY(a.first()->abstractType()->toString().endsWith("int")); 0179 QVERIFY(b.first()->abstractType()->toString().endsWith("int")); 0180 } 0181 0182 void PyDUChainTest::testMultiFromImport_data() { 0183 QTest::addColumn<QString>("code"); 0184 QTest::newRow("multiimport") << "import testMultiFromImport.i.localvar1\n" 0185 "import testMultiFromImport.i.localvar2\n" 0186 "a = testMultiFromImport.i.localvar1\n" 0187 "b = testMultiFromImport.i.localvar2\n"; 0188 } 0189 0190 void PyDUChainTest::testRelativeImport() 0191 { 0192 QFETCH(QString, code); 0193 QFETCH(QString, token); 0194 QFETCH(QString, type); 0195 ReferencedTopDUContext ctx = parse(code); 0196 QVERIFY(ctx); 0197 DUChainReadLocker lock; 0198 QList<Declaration*> t = ctx->findDeclarations(QualifiedIdentifier(token)); 0199 QVERIFY(! t.isEmpty()); 0200 QVERIFY(t.first()->abstractType()->toString().endsWith(type)); 0201 } 0202 0203 void PyDUChainTest::testRelativeImport_data() { 0204 QTest::addColumn<QString>("code"); 0205 QTest::addColumn<QString>("token"); 0206 QTest::addColumn<QString>("type"); 0207 QTest::newRow(".local") << "from testRelativeImport.m.sm1.go import i1" << "i1" << "int"; 0208 QTest::newRow(".init") << "from testRelativeImport.m.sm1.go import i2" << "i2" << "int"; 0209 QTest::newRow("..local") << "from testRelativeImport.m.sm1.go import i3" << "i3" << "int"; 0210 QTest::newRow("..init") << "from testRelativeImport.m.sm1.go import i4" << "i4" << "int"; 0211 QTest::newRow("..sub.local") << "from testRelativeImport.m.sm1.go import i5" << "i5" << "int"; 0212 QTest::newRow("..sub.init") << "from testRelativeImport.m.sm1.go import i6" << "i6" << "int"; 0213 } 0214 0215 void PyDUChainTest::testImportFiles() { 0216 QString code = "import testImportFiles\nk = testImportFiles.fromInit()\np = testImportFiles.other.fromOther()"; 0217 ReferencedTopDUContext ctx = parse(code.toUtf8()); 0218 DUChainReadLocker lock; 0219 QVERIFY(ctx); 0220 0221 auto k = ctx->findDeclarations(QualifiedIdentifier("k")); 0222 auto p = ctx->findDeclarations(QualifiedIdentifier("p")); 0223 QCOMPARE(k.size(), 1); 0224 QCOMPARE(p.size(), 1); 0225 QVERIFY(k.first()->abstractType()); 0226 QCOMPARE(k.first()->abstractType()->toString(), QString("fromInit")); 0227 QCOMPARE(p.first()->abstractType()->toString(), QString("fromOther")); 0228 } 0229 0230 void PyDUChainTest::testCrashes() { 0231 QFETCH(QString, code); 0232 ReferencedTopDUContext ctx = parse(code); 0233 QVERIFY(ctx); 0234 QVERIFY(m_ast); 0235 QVERIFY(! m_ast->body.isEmpty()); 0236 } 0237 0238 void PyDUChainTest::testCrashes_data() { 0239 QTest::addColumn<QString>("code"); 0240 0241 QTest::newRow("composite_slice") << "A = M[1:3, 3]"; 0242 QTest::newRow("unicode_char") << "a = \"í\""; 0243 QTest::newRow("unicode escape char") << "print(\"\\xe9\")"; 0244 QTest::newRow("augassign") << "a = 3\na += 5"; 0245 QTest::newRow("delete") << "a = 3\ndel a"; 0246 QTest::newRow("double_comprehension") << "q = [[x for x in a] + [x for x in a] for y in b]"; 0247 QTest::newRow("for_else") << "for i in range(3): pass\nelse: pass"; 0248 QTest::newRow("for_while") << "while i < 4: pass\nelse: pass"; 0249 QTest::newRow("ellipsis") << "a[...]"; 0250 QTest::newRow("tuple_assign_unknown") << "foo = (unknown, unknown, unknown)"; 0251 QTest::newRow("for_assign_unknown") << "for foo, bar, baz in unknown: pass"; 0252 QTest::newRow("negative slice index") << "t = (1, 2, 3)\nd = t[-1]"; 0253 QTest::newRow("decorator_with_args") << "@foo('bar', 'baz')\ndef myfunc(): pass"; 0254 QTest::newRow("non_name_decorator") << "@foo.crazy_decorators\ndef myfunc(): pass"; 0255 QTest::newRow("static_method") << "class c:\n @staticmethod\n def method(): pass"; 0256 QTest::newRow("vararg_in_middle") << "def func(a, *b, c): pass\nfunc(1, 2, 3, 4, 5)"; 0257 QTest::newRow("whatever") << "for attr in updated:\n " 0258 " getattr.update"; 0259 QTest::newRow("return_outside_function") << "return 3"; 0260 QTest::newRow("return_context_outside_function") << "return [x for x in range(3)]"; 0261 QTest::newRow("paren_attrib_access") << "a = (xxx or yyy).zzz"; 0262 QTest::newRow("func_call") << "a = xxx.func(yyy.zzz)"; 0263 QTest::newRow("comprehension_attrib") << "a = [foo for foo in bar].baz"; 0264 QTest::newRow("comprehension_attrib2") << "a = [foo.bar for foo in bar]"; 0265 QTest::newRow("lambda_cmpr_defarg") << "a = lambda foo=[b for b in (1, 2, 3)]: foo"; 0266 QTest::newRow("attrib") << "(sep or ' ').join(xxxx.capitalize() for xxxx in ssss.split(sep))"; 0267 QTest::newRow("attrib2") << "(sep or ' ').join(x.capitalize() for x in s.split(sep))"; 0268 QTest::newRow("attrib3") << "known_threads = {line.strip()}"; 0269 QTest::newRow("attrib4") << "known_threads = {line.strip() for line in [\"foo\"] if line.strip()}"; 0270 QTest::newRow("stacked_lambdas") << "l4 = lambda x = lambda y = lambda z=1 : z : y() : x()"; 0271 QTest::newRow("newline_attrib2") << "raise TypeError(\"argument should be a bound method, not {}\"\n" 0272 ".format(type(meth))) from None"; 0273 QTest::newRow("newline_attrib") << "some_instance \\\n" 0274 ". attr1 \\\n" 0275 ".funcfunc(argarg, arg2arg) \\\n" 0276 ".foo"; 0277 QTest::newRow("fancy generator context range") << "c1_list = sorted(letter for (letter, meanings) \\\n" 0278 "in ambiguous_nucleotide_values.iteritems() \\\n" 0279 "if set([codon[0] for codon in codons]).issuperset(set(meanings)))"; 0280 QTest::newRow("fancy class range") << "class SchemeLexer(RegexLexer):\n" 0281 " valid_name = r'[a-zA-Z0-9!$%&*+,/:<=>?@^_~|-]+'\n" 0282 "\n" 0283 " tokens = {\n" 0284 " 'root' : [\n" 0285 " # the comments - always starting with semicolon\n" 0286 " # and going to the end of the line\n" 0287 " (r';.*$', Comment.Single),\n" 0288 "\n" 0289 " # whitespaces - usually not relevant\n" 0290 " (r'\\s+', Text),\n" 0291 "\n" 0292 " # numbers\n" 0293 " (r'-?\\d+\\.\\d+', Number.Float),\n" 0294 " (r'-?\\d+', Number.Integer)\n" 0295 " ],\n" 0296 " }\n"; 0297 QTest::newRow("another fancy range") << "setup_args['data_files'] = [\n" 0298 " (os.path.dirname(os.path.join(install_base_dir, pattern)),\n" 0299 " [ f for f in glob.glob(pattern) ])\n" 0300 " for pattern in patterns\n" 0301 "]\n"; 0302 QTest::newRow("kwarg_empty_crash") << "def myfun(): return\ncheckme = myfun(kw=something)"; 0303 QTest::newRow("stacked_tuple_hang") << "tree = (1,(2,(3,(4,(5,'Foo')))))"; 0304 QTest::newRow("stacked_tuple_hang2") << "tree = (257," 0305 "(264," 0306 "(285," 0307 "(259," 0308 "(272," 0309 "(275," 0310 "(1, 'return')))))))"; 0311 QTest::newRow("very_large_tuple_hang") << "tree = " 0312 "(257," 0313 "(264," 0314 "(285," 0315 "(259," 0316 "(1, 'def')," 0317 "(1, 'f')," 0318 "(260, (7, '('), (8, ')'))," 0319 "(11, ':')," 0320 "(291," 0321 "(4, '')," 0322 "(5, '')," 0323 "(264," 0324 "(265," 0325 "(266," 0326 "(272," 0327 "(275," 0328 "(1, 'return')," 0329 "(313," 0330 "(292," 0331 "(293," 0332 "(294," 0333 "(295," 0334 "(297," 0335 "(298," 0336 "(299," 0337 "(300," 0338 "(301," 0339 "(302, (303, (304, (305, (2, '1'))))))))))))))))))," 0340 "(264," 0341 "(265," 0342 "(266," 0343 "(272," 0344 "(276," 0345 "(1, 'yield')," 0346 "(313," 0347 "(292," 0348 "(293," 0349 "(294," 0350 "(295," 0351 "(297," 0352 "(298," 0353 "(299," 0354 "(300," 0355 "(301," 0356 "(302," 0357 "(303, (304, (305, (2, '1'))))))))))))))))))," 0358 "(4, '')))," 0359 "(6, '')))))," 0360 "(4, '')," 0361 "(0, ''))))"; 0362 QTest::newRow("attribute_hang") << "s = \"123\"\n" 0363 "s = s.replace(u'ł', 'l').\\\n" 0364 "replace(u'ó', 'o').\\\n" 0365 "replace(u'ą', 'a').\\\n" 0366 "replace(u'ę', 'e').\\\n" 0367 "replace(u'ś', 's').\\\n" 0368 "replace(u'ż', 'z').\\\n" 0369 "replace(u'ź', 'z').\\\n" 0370 "replace(u'ć', 'c').\\\n" 0371 "replace(u'ń', 'n').\\\n" 0372 "replace(u'б', 'b').\\\n" 0373 "replace(u'в', 'v').\\\n" 0374 "replace(u'г', 'g').\\\n" 0375 "replace(u'д', 'd').\\\n" 0376 "replace(u'ё', 'yo').\\\n" 0377 "replace(u'ć', 'c').\\\n" 0378 "replace(u'ń', 'n').\\\n" 0379 "replace(u'б', 'b').\\\n" 0380 "replace(u'в', 'v').\\\n" 0381 "replace(u'г', 'g').\\\n" 0382 "replace(u'д', 'd').\\\n" 0383 "replace(u'ё', 'yo').\\\n" 0384 "replace(u'ć', 'c').\\\n" 0385 "replace(u'ń', 'n').\\\n" 0386 "replace(u'б', 'b').\\\n" 0387 "replace(u'в', 'v').\\\n" 0388 "replace(u'г', 'g').\\\n" 0389 "replace(u'д', 'd').\\\n" 0390 "replace(u'ё', 'yo')\n"; 0391 QTest::newRow("function context range crash") << "def myfunc(arg):\n foo = 3 + \\\n[x for x in range(20)]"; 0392 QTest::newRow("decorator comprehension crash") << "@implementer_only(interfaces.ISSLTransport,\n" 0393 " *[i for i in implementedBy(tcp.Client)\n" 0394 " if i != interfaces.ITLSTransport])\n" 0395 "class Client(tcp.Client):\n" 0396 " pass\n"; 0397 QTest::newRow("comprehension_as_default_crash") << "def foo(bar = [item for (_, item) in items()]):\n return"; 0398 QTest::newRow("try_except") << "try: pass\nexcept: pass"; 0399 QTest::newRow("try_except_type") << "try: pass\nexcept FooException: pass"; 0400 QTest::newRow("try_except_type_as") << "try: pass\nexcept FooException as bar: pass"; 0401 QTest::newRow("import_missing") << "from this_does_not_exist import nor_does_this"; 0402 0403 QTest::newRow("list_append_missing") << "foo = []\nfoo.append(missing)"; 0404 QTest::newRow("list_append_missing_arg") << "foo = []\nfoo.append()"; 0405 0406 QTest::newRow("list_extend_missing") << "foo = []\nfoo.extend(missing)"; 0407 QTest::newRow("list_extend_missing_arg") << "foo = []\nfoo.extend()"; 0408 QTest::newRow("method_of_call_with_list_arg") << 0409 "class MyClass:\n" 0410 " def bar(self): pass\n" 0411 "def foo(x):\n" 0412 " return MyClass()\n" 0413 "foo([0]).bar()"; 0414 QTest::newRow("unpacked_dict_kwarg") << "def foo(arg): pass\nfoo(**{'arg': 2})"; 0415 QTest::newRow("negative_container_hints") << 0416 "class Evil:\n" 0417 " def aa(self, arg):\n" 0418 " \"\"\"! addsTypeOfArgContent ! -1\"\"\"\n" 0419 " def bb(self, arg):\n" 0420 " \"\"\"! addsTypeOfArg ! -2\"\"\"\n" 0421 " def cc(self, arg):\n" 0422 " \"\"\"! returnContentEqualsContentOf ! -3\"\"\"\n" 0423 "e = Evil()\n" 0424 "z = [e.aa(1), e.bb(2), e.cc(3)]"; 0425 QTest::newRow("comprehension_in_lambda") << "lambda foo: [bar for bar in foo]"; 0426 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 6, 0) 0427 QTest::newRow("comprehension_in_fstring") << 0428 "def crash(): return f'expr={ {x: y for x, y in [(1, 2), ]}}'"; 0429 QTest::newRow("comprehension_in_annassign_1") << "foo: int = [x for x in (42,)][0]"; 0430 QTest::newRow("comprehension_in_annassign_2") << "foo: [t for t in (int,)][0] = 42"; 0431 QTest::newRow("lambda_in_annassign_1") << "foo: int = (lambda: 42)()"; 0432 QTest::newRow("lambda_in_annassign_2") << "foo: (lambda: int)() = 42"; 0433 #endif 0434 QTest::newRow("definition_in_baseclass_1") << "class Foo(lambda x: 1): pass"; 0435 QTest::newRow("definition_in_baseclass_2") << "class Foo([x for x in (1, 2)]): pass"; 0436 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 10, 0) 0437 QTest::newRow("match") << "match x.split():\n case [a, b]: pass"; 0438 #endif 0439 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 11, 0) 0440 QTest::newRow("except_star") << "try: pass\nexcept* (IndexError, ValueError): pass"; 0441 #endif 0442 } 0443 0444 void PyDUChainTest::testClassVariables() 0445 { 0446 ReferencedTopDUContext ctx = parse("class c():\n myvar = 3;\n def meth(self):\n print(myvar)"); 0447 QVERIFY(ctx.data()); 0448 DUChainWriteLocker lock(DUChain::lock()); 0449 CursorInRevision relevantPosition(3, 10); 0450 DUContext* c = ctx->findContextAt(relevantPosition); 0451 QVERIFY(c); 0452 int useIndex = c->findUseAt(relevantPosition); 0453 if ( useIndex != -1 ) { 0454 QVERIFY(useIndex < c->usesCount()); 0455 const Use* u = &(c->uses()[useIndex]); 0456 QVERIFY(!u->usedDeclaration(c->topContext())); 0457 } 0458 } 0459 0460 void PyDUChainTest::testWarnNewNotCls() 0461 { 0462 QFETCH(QString, code); 0463 QFETCH(int, probs); 0464 0465 ReferencedTopDUContext ctx = parse(code); 0466 DUChainReadLocker lock; 0467 QCOMPARE(ctx->problems().count(), probs); 0468 } 0469 0470 void PyDUChainTest::testWarnNewNotCls_data() 0471 { 0472 QTest::addColumn<QString>("code"); 0473 QTest::addColumn<int>("probs"); 0474 0475 QTest::newRow("check_for_new_first_arg_cls") << "class c():\n def __new__(clf, other):\n pass" << 1; 0476 QTest::newRow("check_for_new_first_arg_cls_0") << "class c():\n def __new__(cls, other):\n pass" << 0; 0477 QTest::newRow("check_first_arg_class_self") << "class c():\n def test(seff, masik):\n pass" << 1; 0478 QTest::newRow("check_first_arg_class_self_0") << "class c():\n def test(self, masik):\n pass" << 0; 0479 } 0480 0481 // this is actually for both binary and boolean operators 0482 void PyDUChainTest::testBinaryOperatorsUnsure() 0483 { 0484 QFETCH(QString, code); 0485 QFETCH(QString, type); 0486 0487 ReferencedTopDUContext ctx = parse(code); 0488 DUChainWriteLocker lock; 0489 QList<Declaration*> ds = ctx->findDeclarations(QualifiedIdentifier("checkme")); 0490 QVERIFY(!ds.isEmpty()); 0491 Declaration* d = ds.first(); 0492 QVERIFY(d); 0493 QVERIFY(d->abstractType()); 0494 QCOMPARE(d->abstractType()->toString(), type); 0495 } 0496 0497 void PyDUChainTest::testBinaryOperatorsUnsure_data() 0498 { 0499 QTest::addColumn<QString>("code"); 0500 QTest::addColumn<QString>("type"); 0501 0502 QTest::newRow("check_unsure_type_0") << "class c():\n def __mul__(self, other):\n return int();\nx = c();\nx = 3.5;\ny = 3;\ncheckme = x * y;" << "unsure (float, int)"; 0503 QTest::newRow("check_unsure_type_1") << "class c():\n def __mul__(self, other):\n return int();\nx = c();\nx = 3;\ny = 3;\ncheckme = x * y;" << "int"; 0504 QTest::newRow("check_unsure_type_2") << "class c():\n pass;\nx = c();\nx = 3;\ny = 3;\ncheckme = x * y;" << "int"; 0505 QTest::newRow("check_unsure_type_3") << "class c():\n pass;\nclass d():\n pass;\nx = c();\nx = d();\ny = 3;\ncheckme = x * y;" << "int"; 0506 0507 QTest::newRow("check_unsure_type_4") << "checkme = True or False" << "bool"; 0508 QTest::newRow("check_unsure_type_5") << "a = 'foo'; checkme = a or 'bar';" << "str"; 0509 QTest::newRow("check_unsure_type_6") << "class A(): pass\nclass B(): pass;\ncheckme = A() or B() or 42;" << "unsure (A, B, int)"; 0510 0511 } 0512 0513 0514 void PyDUChainTest::testFlickering() 0515 { 0516 QFETCH(QStringList, code); 0517 QFETCH(int, before); 0518 QFETCH(int, after); 0519 0520 TestFile f(code[0], "py"); 0521 f.parse(TopDUContext::ForceUpdate); 0522 f.waitForParsed(500); 0523 0524 ReferencedTopDUContext ctx = f.topContext(); 0525 QVERIFY(ctx); 0526 0527 DUChainWriteLocker lock(DUChain::lock()); 0528 int count = ctx->localDeclarations().size(); 0529 qDebug() << "Declaration count before: " << count; 0530 QVERIFY(count == before); 0531 lock.unlock(); 0532 0533 f.setFileContents(code[1]); 0534 f.parse(TopDUContext::ForceUpdate); 0535 f.waitForParsed(500); 0536 ctx = f.topContext(); 0537 QVERIFY(ctx); 0538 0539 lock.lock(); 0540 count = ctx->localDeclarations().size(); 0541 qDebug() << "Declaration count afterwards: " << count; 0542 QVERIFY(count == after); 0543 0544 foreach(Declaration* dec, ctx->localDeclarations()) { 0545 qDebug() << dec->toString() << dec->range(); 0546 qDebug() << dec->uses().size(); 0547 } 0548 } 0549 0550 void PyDUChainTest::testFlickering_data() 0551 { 0552 QTest::addColumn<QStringList>("code"); 0553 QTest::addColumn<int>("before"); 0554 QTest::addColumn<int>("after"); 0555 0556 QTest::newRow("declaration_flicker") << ( QStringList() << "a=2\n" << "b=3\na=2\n" ) << 1 << 2; 0557 } 0558 0559 void PyDUChainTest::testCannotOverwriteBuiltins() 0560 { 0561 QFETCH(QString, code); 0562 QFETCH(QString, expectedType); 0563 0564 ReferencedTopDUContext ctx = parse(code); 0565 DUChainWriteLocker lock; 0566 QList<Declaration*> ds = ctx->findDeclarations(QualifiedIdentifier("checkme")); 0567 QVERIFY(!ds.isEmpty()); 0568 Declaration* d = ds.first(); 0569 QVERIFY(d); 0570 QVERIFY(d->abstractType()); 0571 QCOMPARE(d->abstractType()->toString(), expectedType); 0572 } 0573 0574 void PyDUChainTest::testCannotOverwriteBuiltins_data() 0575 { 0576 QTest::addColumn<QString>("code"); 0577 QTest::addColumn<QString>("expectedType"); 0578 0579 QTest::newRow("list_assign") << "class list(): pass\ncheckme = []\ncheckme.append(3)" << "list of int"; 0580 QTest::newRow("str_assign") << "str = 5; checkme = 'Foo'" << "str"; 0581 QTest::newRow("str_assign2") << "class Foo: pass\nstr = Foo; checkme = 'Foo'" << "str"; 0582 QTest::newRow("str_assign3") << "from testCannotOverwriteBuiltins.i import Foo as str\ncheckme = 'Foo'" << "str"; 0583 QTest::newRow("for") << "for str in [1, 2, 3]: pass\ncheckme = 'Foo'" << "str"; 0584 QTest::newRow("assert") << "assert isinstance(str, int)\ncheckme = 'Foo'" << "str"; 0585 QTest::newRow("assert2") << "assert isinstance(str, int)\ncheckme = 3" << "int"; 0586 QTest::newRow("can_have_custom") << "from testCannotOverwriteBuiltins import mod\ncheckme = mod.open()" << "int"; 0587 QTest::newRow("can_have_custom2") << "from testCannotOverwriteBuiltins import mod\ncheckme = open().read()" << "str"; 0588 QTest::newRow("can_have_custom3") << "from testCannotOverwriteBuiltins import mod\ncheckme = mod.open().read()" << "mixed"; 0589 } 0590 0591 void PyDUChainTest::testVarKWArgs() 0592 { 0593 ReferencedTopDUContext ctx = parse("def myfun(arg, *vararg, **kwarg):\n pass\n pass"); 0594 DUChainWriteLocker lock; 0595 QVERIFY(ctx); 0596 DUContext* func = ctx->findContextAt(CursorInRevision(1, 0)); 0597 QVERIFY(func); 0598 QVERIFY(! func->findDeclarations(QualifiedIdentifier("arg")).isEmpty()); 0599 QVERIFY(! func->findDeclarations(QualifiedIdentifier("vararg")).isEmpty()); 0600 QVERIFY(! func->findDeclarations(QualifiedIdentifier("kwarg")).isEmpty()); 0601 QVERIFY(func->findDeclarations(QualifiedIdentifier("vararg")).first()->abstractType()->toString().startsWith("tuple")); 0602 QCOMPARE(func->findDeclarations(QualifiedIdentifier("kwarg")).first()->abstractType()->toString(), 0603 QString("dict of str : unknown")); 0604 } 0605 0606 void PyDUChainTest::testSimple() 0607 { 0608 QFETCH(QString, code); 0609 QFETCH(int, decls); 0610 QFETCH(int, uses); 0611 0612 ReferencedTopDUContext ctx = parse(code); 0613 DUChainWriteLocker lock(DUChain::lock()); 0614 QVERIFY(ctx); 0615 0616 QVector< Declaration* > declarations = ctx->localDeclarations(); 0617 0618 QCOMPARE(declarations.size(), decls); 0619 0620 int usesCount = 0; 0621 foreach(Declaration* d, declarations) { 0622 usesCount += d->uses().size(); 0623 0624 QVERIFY(d->abstractType()); 0625 } 0626 0627 QCOMPARE(usesCount, uses); 0628 } 0629 0630 void PyDUChainTest::testSimple_data() 0631 { 0632 QTest::addColumn<QString>("code"); 0633 QTest::addColumn<int>("decls"); 0634 QTest::addColumn<int>("uses"); 0635 0636 QTest::newRow("assign") << "b = 2;" << 1 << 0; 0637 QTest::newRow("assign_str") << "b = 'hola';" << 1 << 0; 0638 QTest::newRow("op") << "a = 3; b = a+2;" << 2 << 1; 0639 QTest::newRow("bool") << "a = True" << 1 << 0; 0640 QTest::newRow("op") << "a = True and True;" << 1 << 0; 0641 } 0642 0643 class AttributeRangeTestVisitor : public AstDefaultVisitor { 0644 public: 0645 bool found; 0646 KTextEditor::Range searchingForRange; 0647 QString searchingForIdentifier; 0648 void visitAttribute(AttributeAst* node) override { 0649 auto r = KTextEditor::Range(0, node->startCol, 0, node->endCol); 0650 qDebug() << "Found attr: " << r << node->attribute->value << ", looking for: " << searchingForRange << searchingForIdentifier; 0651 if ( r == searchingForRange && node->attribute->value == searchingForIdentifier ) { 0652 found = true; 0653 return; 0654 } 0655 AstDefaultVisitor::visitAttribute(node); 0656 } 0657 void visitFunctionDefinition(FunctionDefinitionAst* node) override { 0658 auto r = KTextEditor::Range(0, node->name->startCol, 0, node->name->endCol); 0659 qDebug() << "Found func: " << r << node->name->value << ", looking for: " << searchingForRange << searchingForIdentifier; 0660 qDebug() << node->arguments->vararg << node->arguments->kwarg; 0661 if ( r == searchingForRange && node->name->value == searchingForIdentifier ) { 0662 found = true; 0663 return; 0664 } 0665 if ( node->arguments->vararg ) { 0666 auto r = KTextEditor::Range(0, node->arguments->vararg->startCol, 0, node->arguments->vararg->startCol+node->arguments->vararg->argumentName->value.length()); 0667 qDebug() << "Found vararg: " << node->arguments->vararg->argumentName->value << r; 0668 if ( r == searchingForRange && node->arguments->vararg->argumentName->value == searchingForIdentifier ) { 0669 found = true; 0670 return; 0671 } 0672 } 0673 if ( node->arguments->kwarg ) { 0674 auto r = KTextEditor::Range(0, node->arguments->kwarg->startCol, 0, node->arguments->kwarg->startCol+node->arguments->kwarg->argumentName->value.length()); 0675 qDebug() << "Found kwarg: " << node->arguments->kwarg->argumentName->value << r; 0676 if ( r == searchingForRange && node->arguments->kwarg->argumentName->value == searchingForIdentifier ) { 0677 found = true; 0678 return; 0679 } 0680 } 0681 AstDefaultVisitor::visitFunctionDefinition(node); 0682 } 0683 void visitClassDefinition(ClassDefinitionAst* node) override { 0684 auto r = KTextEditor::Range(0, node->name->startCol, 0, node->name->endCol); 0685 qDebug() << "Found cls: " << r << node->name->value << ", looking for: " << searchingForRange << searchingForIdentifier; 0686 if ( r == searchingForRange && node->name->value == searchingForIdentifier ) { 0687 found = true; 0688 return; 0689 } 0690 AstDefaultVisitor::visitClassDefinition(node); 0691 } 0692 void visitImport(ImportAst* node) override { 0693 foreach ( const AliasAst* name, node->names ) { 0694 if ( name->name ) { 0695 qDebug() << "found import" << name->name->value << name->name->range(); 0696 } 0697 if ( name->name && name->name->value == searchingForIdentifier && name->name->range() == searchingForRange ) { 0698 found = true; 0699 return; 0700 } 0701 if ( name->asName ) { 0702 qDebug() << "found import" << name->asName->value << name->asName->range(); 0703 } 0704 if ( name->asName && name->asName->value == searchingForIdentifier && name->asName->range() == searchingForRange ) { 0705 found = true; 0706 return; 0707 } 0708 } 0709 } 0710 }; 0711 0712 void PyDUChainTest::testRanges() 0713 { 0714 QFETCH(QString, code); 0715 QFETCH(int, expected_amount_of_variables); Q_UNUSED(expected_amount_of_variables); 0716 QFETCH(QStringList, column_ranges); 0717 0718 0719 ReferencedTopDUContext ctx = parse(code); 0720 QVERIFY(ctx); 0721 0722 QVERIFY(m_ast); 0723 0724 for ( int i = 0; i < column_ranges.length(); i++ ) { 0725 int scol = column_ranges.at(i).split(",")[0].toInt(); 0726 int ecol = column_ranges.at(i).split(",")[1].toInt(); 0727 QString identifier = column_ranges.at(i).split(",")[2]; 0728 auto r = KTextEditor::Range(0, scol, 0, ecol); 0729 0730 AttributeRangeTestVisitor* visitor = new AttributeRangeTestVisitor(); 0731 visitor->searchingForRange = r; 0732 visitor->searchingForIdentifier = identifier; 0733 visitor->visitCode(m_ast.data()); 0734 QEXPECT_FAIL("attr_dot_name_hash", "Insufficiently magic hack", Continue); 0735 QCOMPARE(visitor->found, true); 0736 delete visitor; 0737 } 0738 } 0739 0740 void PyDUChainTest::testRanges_data() 0741 { 0742 QTest::addColumn<QString>("code"); 0743 QTest::addColumn<int>("expected_amount_of_variables"); 0744 QTest::addColumn<QStringList>("column_ranges"); 0745 0746 QTest::newRow("attr_two_attributes") << "base.attr" << 2 << ( QStringList() << "5,8,attr" ); 0747 QTest::newRow("attr_binary") << "base.attr + base.attr" << 4 << ( QStringList() << "5,8,attr" << "17,20,attr" ); 0748 QTest::newRow("attr_same") << "aaa.aaa.aaa + aaa.aaa.aaa" << 6 << ( QStringList() << "4,6,aaa" << "8,10,aaa" << "18,20,aaa" << "22,24,aaa" ); 0749 QTest::newRow("attr_three_attributes") << "base.attr.subattr" << 3 << ( QStringList() << "5,8,attr" << "10,16,subattr" ); 0750 QTest::newRow("attr_functionCall") << "base.attr().subattr" << 3 << ( QStringList() << "5,8,attr" << "12,18,subattr" ); 0751 QTest::newRow("attr_stringSubscript") << "base.attr[\"a.b.c..de\"].subattr" << 3 << ( QStringList() << "5,8,attr" << "23,29,subattr" ); 0752 QTest::newRow("attr_functionCallWithArguments") << "base.attr(arg1, arg2).subattr" << 5 << ( QStringList() << "5,8,attr" << "22,28,subattr" ); 0753 QTest::newRow("attr_functionCallWithArgument_withInner") << "base.attr(arg1.parg2).subattr" << 5 << ( QStringList() << "5,8,attr" << "22,28,subattr" << "15,19,parg2" ); 0754 QTest::newRow("attr_complicated") << "base.attr(arg1.arg2(arg4.arg5, [func(a.b)]).arg3(arg6.arg7)).subattr" << 5 << ( QStringList() << "5,8,attr" << "15,18,arg2" << 0755 "25,28,arg5" << "39,39,b" << "44,47,arg3" << "54,57,arg7" << "61,67,subattr"); 0756 QTest::newRow("attr_two_in_call") << "func(inst.aaa, inst.bbbb)" << 2 << ( QStringList() << "10,12,aaa" << "20,23,bbbb" ); 0757 QTest::newRow("attr_two_in_call_same") << "func(inst.aaa, inst.aaaa)" << 2 << ( QStringList() << "10,12,aaa" << "20,23,aaaa" ); 0758 QTest::newRow("attr_two_in_call_same2") << "func(inst.aaa, inst.aaa)" << 2 << ( QStringList() << "10,12,aaa" << "20,22,aaa" ); 0759 QTest::newRow("attr_of_string_slash") << "'/'.join(a)" << 1 << ( QStringList() << "4,7,join" ); 0760 QTest::newRow("attr_of_string_in_list") << "[\"*{0}*\".format(foo)]" << 1 << ( QStringList() << "9,14,format" ); 0761 QTest::newRow("attr_of_call_in_list") << "[foo().format(foo)]" << 1 << ( QStringList() << "7,12,format" ); 0762 QTest::newRow("attr_parentheses") << "(\"foo\" + \"foo\").capitalize()" << 1 << ( QStringList() << "16,25,capitalize" ); 0763 QTest::newRow("attr_commented_name") << "base.attr # attr" << 2 << ( QStringList() << "5,8,attr" ); 0764 QTest::newRow("attr_name_in_strings") << "'attr' + base['attr'].attr # attr" << 4 << ( QStringList() << "22,25,attr" ); 0765 QTest::newRow("attr_dot_hash_in_strings") << "'.foo#' + base['.#'].attr # attr" << 4 << ( QStringList() << "21,24,attr" ); 0766 QTest::newRow("attr_dot_name_hash") << "base['.attr#'].attr" << 4 << ( QStringList() << "15,18,attr" ); 0767 0768 QTest::newRow("string_parentheses") << "(\"asdf\".join())" << 1 << ( QStringList() << "8,11,join" ); 0769 QTest::newRow("string_parentheses2") << "(\"asdf\").join()" << 1 << ( QStringList() << "9,12,join" ); 0770 QTest::newRow("string_parentheses3") << "(\"asdf\".join()).join()" << 2 << ( QStringList() << "8,11,join" << "16,19,join" ); 0771 QTest::newRow("string_parentheses4") << "(\"asdf\".join()+2).join()" << 2 << ( QStringList() << "8,11,join" << "18,21,join" ); 0772 QTest::newRow("string_parentheses_call") << "f(\"asdf\".join())" << 1 << ( QStringList() << "9,12,join" ); 0773 0774 QTest::newRow("funcrange_def") << "def func(): pass" << 1 << ( QStringList() << "4,7,func" ); 0775 QTest::newRow("funcrange_spaces_def") << "def func(): pass" << 1 << ( QStringList() << "7,10,func" ); 0776 QTest::newRow("classdef_range") << "class cls(): pass" << 1 << ( QStringList() << "6,8,cls" ); 0777 QTest::newRow("classdef_range_inheritance") << "class cls(parent1, parent2): pass" << 1 << ( QStringList() << "6,8,cls" ); 0778 QTest::newRow("classdef_range_inheritance_spaces") << "class cls( parent1, parent2 ):pass" << 1 << ( QStringList() << "12,14,cls" ); 0779 QTest::newRow("vararg_kwarg") << "def func(*vararg, **kwargs): pass" << 2 << ( QStringList() << "10,16,vararg" << "20,26,kwargs" ); 0780 0781 QTest::newRow("import") << "import sys" << 1 << ( QStringList() << "7,10,sys" ); 0782 QTest::newRow("import2") << "import i.localvar1" << 1 << ( QStringList() << "7,18,i.localvar1" ); 0783 QTest::newRow("import3") << "import sys as a" << 1 << ( QStringList() << "13,14,a" ); 0784 } 0785 0786 class TypeTestVisitor : public AstDefaultVisitor { 0787 public: 0788 QString searchingForType; 0789 TopDUContextPointer ctx; 0790 bool found; 0791 void visitName(NameAst* node) override { 0792 if ( node->identifier->value != "checkme" ) return; 0793 QList<Declaration*> decls = ctx->findDeclarations(QualifiedIdentifier(node->identifier->value)); 0794 if ( ! decls.length() ) { 0795 qCDebug(KDEV_PYTHON_DUCHAIN) << "No declaration found for " << node->identifier->value; 0796 return; 0797 } 0798 Declaration* d = decls.last(); 0799 QVERIFY(d->abstractType()); 0800 qCDebug(KDEV_PYTHON_DUCHAIN) << "found: " << node->identifier->value << "is" << d->abstractType()->toString() << "should be" << searchingForType; 0801 if ( d->abstractType()->toString().replace("__kdevpythondocumentation_builtin_", "").startsWith(searchingForType) ) { 0802 found = true; 0803 return; 0804 } 0805 }; 0806 }; 0807 0808 void PyDUChainTest::testTypes() 0809 { 0810 0811 QFETCH(QString, code); 0812 QFETCH(QString, expectedType); 0813 0814 ReferencedTopDUContext ctx = parse(code.toUtf8()); 0815 QVERIFY(ctx); 0816 QVERIFY(m_ast); 0817 0818 DUChainReadLocker lock(DUChain::lock()); 0819 TypeTestVisitor* visitor = new TypeTestVisitor(); 0820 visitor->ctx = TopDUContextPointer(ctx.data()); 0821 visitor->searchingForType = expectedType; 0822 visitor->visitCode(m_ast.data()); 0823 QEXPECT_FAIL("tuple_func", "no suitable docstring hint", Continue); 0824 QEXPECT_FAIL("tuple_add", "not implemented", Continue); 0825 QEXPECT_FAIL("tuple_mul", "not implemented", Continue); 0826 QEXPECT_FAIL("return_builtin_iterator", "fake builtin iter()", Continue); 0827 QEXPECT_FAIL("parent_constructor_arg_type", "Not enough passes?", Continue); 0828 QEXPECT_FAIL("init_class_no_decl", "aliasing info lost", Continue); 0829 QEXPECT_FAIL("property_wrong", "visitCall uses declaration if no type", Continue); 0830 QEXPECT_FAIL("property_setter", "very basic property support", Continue); 0831 QEXPECT_FAIL("assignment_expr_context", "not implemented", Continue); 0832 if (!visitor->found) 0833 qDebug() << m_ast->dump(); 0834 QCOMPARE(visitor->found, true); 0835 } 0836 0837 void PyDUChainTest::testTypes_data() 0838 { 0839 QTest::addColumn<QString>("code"); 0840 QTest::addColumn<QString>("expectedType"); 0841 0842 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 6, 0) 0843 QTest::newRow("annotate_decl") << "checkme: int" << "int"; 0844 QTest::newRow("annotate_assign") << "checkme: int = 3.5" << "unsure (float, int)"; 0845 #endif 0846 QTest::newRow("listtype") << "checkme = []" << "list"; 0847 QTest::newRow("listtype_func") << "checkme = list()" << "list"; 0848 QTest::newRow("listtype_with_contents") << "checkme = [1, 2, 3, 4, 5]" << "list of int"; 0849 QTest::newRow("listtype_extended") << "some_misc_var = []; checkme = some_misc_var" << "list"; 0850 QTest::newRow("dicttype") << "checkme = {}" << "dict"; 0851 QTest::newRow("dicttype_get") << "d = {0.4:5}; checkme = d.get(0)" << "int"; 0852 QTest::newRow("dicttype_func") << "checkme = dict()" << "dict"; 0853 QTest::newRow("dicttype_extended") << "some_misc_var = {}; checkme = some_misc_var" << "dict"; 0854 QTest::newRow("tuple") << "checkme = ()" << "tuple of ()"; 0855 QTest::newRow("tuple_func") << "checkme = tuple((1, 2.3))" << "tuple of (int, float)"; 0856 QTest::newRow("tuple_with_contents") << "checkme = 1, 2.3" << "tuple of (int, float)"; 0857 QTest::newRow("tuple_extended") << "some_misc_var = (); checkme = some_misc_var" << "tuple of ()"; 0858 QTest::newRow("tuple_max_display") << "checkme = 1,2,3,4,5,6" << "tuple of (int, int, int, int, int, ...)"; 0859 QTest::newRow("bool") << "checkme = True" << "bool"; 0860 QTest::newRow("float") << "checkme = 3.7" << "float"; 0861 QTest::newRow("int") << "checkme = 3" << "int"; 0862 QTest::newRow("str") << "checkme = \"foo\"" << "str"; 0863 QTest::newRow("bytes") << "checkme = b\"foo\"" << "bytes"; 0864 0865 QTest::newRow("function_arg_scope") << "class Foo:\n" 0866 " a = 3\n" 0867 " def func(self, x=a):\n" 0868 " return x\n" 0869 "f = Foo()\n" 0870 "checkme = f.func()" << "int"; 0871 0872 QTest::newRow("with") << "with open('foo') as f: checkme = f.read()" << "str"; 0873 QTest::newRow("with_list_target") << "bar = [1, 2, 3]\n" 0874 "with open('foo') as bar[1]: checkme = bar[1].read()" << "str"; 0875 QTest::newRow("with_attr_target") << "bar = object()\n" 0876 "with open('foo') as bar.zep: checkme = bar.zep.read()" << "str"; 0877 QTest::newRow("with_nonself_enter") << // From https://bugs.kde.org/show_bug.cgi?id=399534 0878 "class Mgr:\n" 0879 " def __enter__(self): return 42\n" 0880 " def __exit__(self, *args): pass\n" 0881 "with Mgr() as asd:\n" 0882 " checkme = asd" << "int"; 0883 QTest::newRow("with_tuple_target") << 0884 "class Mgr:\n" 0885 " def __enter__(self): return (42, 3.4)\n" 0886 " def __exit__(self, *args): pass\n" 0887 "with Mgr() as (aa, bb):\n" 0888 " checkme = bb" << "float"; 0889 0890 QTest::newRow("arg_after_vararg") << "def func(x, y, *, z:int): return z\ncheckme = func()" << "int"; 0891 QTest::newRow("arg_after_vararg_with_default") << "def func(x=5, y=3, *, z:int): return z\ncheckme = func()" << "int"; 0892 QTest::newRow("arg_default") << "def func(x=5, z='foo'): return z\ncheckme = func()" << "str"; 0893 QTest::newRow("arg_kw_no_default") << "def func(x=5, *, a, z='foo'): return a\ncheckme = func()" << "mixed"; 0894 QTest::newRow("arg_kw_default_after_no_default") << "def func(x=5, *, a, z='foo'): return z\ncheckme = func()" << "str"; 0895 QTest::newRow("arg_kw_default") << "def func(x=5, *, z='foo'): return z\ncheckme = func()" << "str"; 0896 QTest::newRow("arg_kw_default_after_default") << "def func(x=5, *, y='foo', z=True): return z\ncheckme = func()" << "bool"; 0897 QTest::newRow("kw_default_before_default") << "def func(*, y='foo', z=True): return y\ncheckme = func()" << "str"; 0898 QTest::newRow("kw_default_after_no_default") << "def func(*, y, z=True): return y\ncheckme = func()" << "mixed"; 0899 QTest::newRow("kw_default_after_default") << "def func(*, y='foo', z=True): return z\ncheckme = func()" << "bool"; 0900 0901 QTest::newRow("class_scope_end_inside") << "a = str()\nclass M:\n" 0902 " a = 2\n foo = a\n" 0903 "checkme = M().foo" << "int"; 0904 QTest::newRow("class_scope_end_outside") << "a = str()\nclass M:\n a = 2\ncheckme = a" << "str"; 0905 0906 QTest::newRow("list_access_right_open_slice") << "some_list = []; checkme = some_list[2:]" << "list"; 0907 QTest::newRow("list_access_left_open_slice") << "some_list = []; checkme = some_list[:2]" << "list"; 0908 QTest::newRow("list_access_closed_slice") << "some_list = []; checkme = some_list[2:17]" << "list"; 0909 QTest::newRow("list_access_step") << "some_list = []; checkme = some_list[::2]" << "list"; 0910 QTest::newRow("list_access_singleItem") << "some_list = []; checkme = some_list[42]" << "mixed"; 0911 0912 QTest::newRow("funccall_number") << "def foo(): return 3; \ncheckme = foo();" << "int"; 0913 QTest::newRow("funccall_string") << "def foo(): return 'a'; \ncheckme = foo();" << "str"; 0914 QTest::newRow("funccall_list") << "def foo(): return []; \ncheckme = foo();" << "list"; 0915 QTest::newRow("funccall_dict") << "def foo(): return {}; \ncheckme = foo();" << "dict"; 0916 QTest::newRow("funccall_no_return") << "def foo(): pass\ncheckme = foo()" << "None"; 0917 QTest::newRow("funccall_def_return") << "def foo(): return\ncheckme = foo()" << "None"; 0918 QTest::newRow("funccall_maybe_def_return") << "def foo():\n if False: return\n return 7\ncheckme = foo()" << "unsure (None, int)"; 0919 0920 QTest::newRow("tuple1") << "checkme, foo = 3, \"str\"" << "int"; 0921 QTest::newRow("tuple2") << "foo, checkme = 3, \"str\"" << "str"; 0922 QTest::newRow("tuple2_negative_index") << "foo = (1, 2, 'foo')\ncheckme = foo[-1]" << "str"; 0923 QTest::newRow("tuple_type") << "checkme = 1, 2" << "tuple"; 0924 QTest::newRow("tuple_rhs_unpack") << "foo = 1, 2.5\nbar = 1, *foo, 2\ncheckme = bar[2]" << "float"; 0925 0926 QTest::newRow("dict_iteritems") << "d = {1:2, 3:4}\nfor checkme, k in d.iteritems(): pass" << "int"; 0927 QTest::newRow("enumerate_key") << "d = [str(), str()]\nfor checkme, value in enumerate(d): pass" << "int"; 0928 QTest::newRow("enumerate_value") << "d = [str(), str()]\nfor key, checkme in enumerate(d): pass" << "str"; 0929 QTest::newRow("dict_enumerate") << "d = {1:2, 3:4}\nfor key, checkme in enumerate(d.values()): pass" << "int"; 0930 0931 QTest::newRow("dict_assign_twice") << "d = dict(); d[''] = 0; d = dict(); d[''] = 0; checkme = d" 0932 << "unsure (dict of str : int, dict)"; 0933 0934 QTest::newRow("class_method_import") << "class c:\n attr = \"foo\"\n def m():\n return attr;\n return 3;\ni=c()\ncheckme=i.m()" << "int"; 0935 QTest::newRow("getsListDocstring") << "foo = [1, 2, 3]\ncheckme = foo.reverse()" << "list of int"; 0936 0937 QTest::newRow("str_iter") << "checkme = [char for char in 'Hello, world!']" << "list of str"; 0938 QTest::newRow("str_subscript") << "checkme = 'Hello, world!'[0]" << "str"; 0939 QTest::newRow("bytes_iter") << "checkme = [byte for byte in b'Hello, world!']" << "list of int"; 0940 QTest::newRow("bytes_subscript") << "checkme = b'Hello, world!'[0]" << "int"; 0941 0942 QTest::newRow("fromAssertIsinstance") << "class c(): pass\ncheckme = mixed()\nassert isinstance(checkme, c)\n" << "c"; 0943 QTest::newRow("fromAssertIsinstanceInvalid") << "class c(): pass\ncheckme = mixed()\nassert isinstance(c, checkme)\n" << "mixed"; 0944 QTest::newRow("fromAssertIsinstanceInvalid2") << "class c(): pass\ncheckme = mixed()\nassert isinstance(D, c)\n" << "mixed"; 0945 QTest::newRow("fromAssertIsinstanceInvalid3") << "checkme = int()\nassert isinstance(checkme, X)\n" << "int"; 0946 QTest::newRow("fromAssertIsinstanceInvalid4") << "checkme = int()\nassert isinstance(checkme)\n" << "int"; 0947 QTest::newRow("fromAssertType") << "class c(): pass\ncheckme = mixed()\nassert type(checkme) == c\n" << "c"; 0948 QTest::newRow("fromIfType") << "class c(): pass\ncheckme = mixed()\nif type(checkme) == c: pass\n" << "c"; 0949 QTest::newRow("fromIfIsinstance") << "class c(): pass\ncheckme = mixed()\nif isinstance(checkme, c): pass\n" << "c"; 0950 0951 QTest::newRow("diff_local_classattr") << "class c(): attr = 1\ninst=c()\ncheckme = c.attr" << "int"; 0952 QTest::newRow("diff_local_classattr2") << "local=3\nclass c(): attr = 1\ninst=c()\ncheckme = c.local" << "mixed"; 0953 QTest::newRow("diff_local_classattr3") << "attr=3.5\nclass c(): attr = 1\ninst=c()\ncheckme = c.attr" << "int"; 0954 // QTest::newRow("class_method_self") << "class c:\n def func(checkme, arg, arg2):\n pass\n" << "c"; 0955 // QTest::newRow("funccall_dict") << "def foo(): return foo; checkme = foo();" << (uint) IntegralType::TypeFunction; 0956 0957 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 6, 0) 0958 // With only one subbed value we get a FormattedValue node 0959 QTest::newRow("fstring_formattedvalue") << "name = 'Jim'; checkme = f'{name}'" << "str"; 0960 // Otherwise a JoinedString, with FormattedValues as children. 0961 QTest::newRow("fstring_joinedstring") << "name = 'Jim'; checkme = f'Hello, {name}! Your name is {name}.'" << "str"; 0962 #endif 0963 0964 QTest::newRow("tuple_simple") << "mytuple = 3, 5.5\ncheckme, foobar = mytuple" << "int"; 0965 QTest::newRow("tuple_simple2") << "mytuple = 3, 5.5\nfoobar, checkme = mytuple" << "float"; 0966 QTest::newRow("tuple_simple3") << "mytuple = 3, 5.5, \"str\", 3, \"str\"\na, b, c, d, checkme = mytuple" << "str"; 0967 0968 QTest::newRow("tuple_single") << "checkme = 4," << "tuple"; 0969 QTest::newRow("tuple_single2") << "checkme, = 4," << "int"; 0970 QTest::newRow("tuple_single3") << "mytuple = 4,\ncheckme, = mytuple" << "int"; 0971 0972 QTest::newRow("tuple_ext_unpack") << "mytuple = 3, 5.5\nfoobar, *starred, checkme = mytuple" << "float"; 0973 QTest::newRow("tuple_ext_unpack2") << "mytuple = 3, 5.5\nfoobar, *checkme, another = mytuple" << "list"; 0974 QTest::newRow("tuple_ext_unpack3") << "mytuple = 3, 5.5\nfoobar, *checkme = mytuple" << "list of float"; 0975 QTest::newRow("tuple_ext_unpack4") << "mytuple = 3, 5.5\n*checkme, = mytuple" << "list of unsure (int, float)"; 0976 0977 QTest::newRow("tuple_nested") << "mytuple = 3, ('foo', 5.5)\ncheckme, foobar = mytuple" << "int"; 0978 QTest::newRow("tuple_nested2") << "mytuple = 3, ('foo', 5.5)\nfoobar, (checkme, other) = mytuple" << "str"; 0979 QTest::newRow("tuple_nested3") << "mytuple = ((7, 'foo'), 5.5), 3\n((baz, checkme), other), foo = mytuple" << "str"; 0980 0981 QTest::newRow("tuple_nested_ext") << "mytuple = (2, ('foo', 'bar', 6), 7)\na, (b, *checkme, c), *d = mytuple" << "list of str"; 0982 0983 QTest::newRow("tuple_multi_assign") << "mytuple = 2, 'foo'\ncheckme = a = mytuple" << "tuple"; 0984 QTest::newRow("tuple_multi_assign2") << "mytuple = 2, 'foo'\ncheckme, a = b = mytuple" << "int"; 0985 0986 QTest::newRow("list_unpack") << "mylist = [1, 2, 3]\ncheckme, b, c = mylist" << "int"; 0987 QTest::newRow("list_unpack2") << "mylist = [1, 'x', 3]\ncheckme, b, c = mylist" << "unsure (int, str)"; 0988 0989 QTest::newRow("list_ext_unpack") << "mylist = [1, 2, 3]\n*checkme, foo = mylist" << "list of int"; 0990 QTest::newRow("list_ext_unpack2") << "mylist = [1, 'x', 3]\n*checkme, foo = mylist" << "list of unsure (int, str)"; 0991 0992 QTest::newRow("if_expr_sure") << "checkme = 3 if 7 > 9 else 5" << "int"; 0993 0994 QTest::newRow("unary_op") << "checkme = -42" << "int"; 0995 0996 QTest::newRow("tuple_funcret") << "def myfun(): return 3, 5\ncheckme, a = myfun()" << "int"; 0997 QTest::newRow("tuple_funcret2") << "def myfun():\n t = 3, 5\n return t\ncheckme, a = myfun()" << "int"; 0998 0999 QTest::newRow("yield") << "def myfun():\n yield 3\ncheckme = myfun()" << "list of int"; 1000 QTest::newRow("yield_twice") << "def myfun():\n yield 3\n yield 'foo'\ncheckme = myfun()" << "list of unsure (int, str)"; 1001 // this is mostly a check that it doesn't crash 1002 QTest::newRow("yield_return") << "def myfun():\n return 3\n yield 'foo'\ncheckme = myfun()" << "unsure (int, list of str)"; 1003 1004 QTest::newRow("lambda") << "x = lambda t: 3\ncheckme = x()" << "int"; 1005 QTest::newRow("lambda_failure") << "x = lambda t: 3\ncheckme = t" << "mixed"; 1006 1007 QTest::newRow("function_arg_tuple") << "def func(*arg):\n foo, bar = arg\n return bar\ncheckme = func(3, 5)" << "int"; 1008 QTest::newRow("function_arg_tuple2") << "def func(*arg):\n return arg[-1]\ncheckme = func(3, \"Foo\")" << "str"; 1009 1010 QTest::newRow("tuple_indexaccess") << "t = 3, 5.5\ncheckme = t[0]" << "int"; 1011 QTest::newRow("tuple_indexaccess2") << "t = 3, 5.5\ncheckme = t[1]" << "float"; 1012 QTest::newRow("tuple_indexaccess3") << "t = 3, 4\ncheckme = t[1]" << "int"; 1013 QTest::newRow("tuple_indexaccess4") << "t = 3, 4.5\ncheckme = t[2]" << "unsure (int, float)"; 1014 1015 QTest::newRow("tuple_indexaccess_neg") << "t = 3, 4.5; checkme = t[-1]" << "float"; 1016 QTest::newRow("tuple_indexaccess_neg2") << "t = 3, 4.5; checkme = t[-2]" << "int"; 1017 QTest::newRow("tuple_indexaccess_neg3") << "t = 3, 4.5; checkme = t[-3]" << "unsure (int, float)"; 1018 1019 QTest::newRow("tuple_slice") << "t = 3, 'q', 4.5; checkme = t[-3: 2]" << "tuple of (int, str)"; 1020 QTest::newRow("tuple_slice_normal") << "t = 1, 2.3, 'a', {}; checkme = t[1:3]" << "tuple of (float, str)"; 1021 QTest::newRow("tuple_slice_defstart") << "t = 1, 2.3, 'a', {}; checkme = t[:3]" << "tuple of (int, float, str)"; 1022 QTest::newRow("tuple_slice_defstop") << "t = 1, 2.3, 'a', {}; checkme = t[1:]" << "tuple of (float, str, dict)"; 1023 QTest::newRow("tuple_slice_defboth") << "t = 1, 2.3, 'a', {}; checkme = t[:]" << "tuple of (int, float, str, dict)"; 1024 QTest::newRow("tuple_slice_step") << "t = 1, 2.3, 'a', {}; checkme = t[0:3:2]" << "tuple of (int, str)"; 1025 QTest::newRow("tuple_slice_reverse") << "t = 1, 2.3, 'a', {}; checkme = t[3:1:-1]" << "tuple of (dict, str)"; 1026 QTest::newRow("tuple_slice_revstart") << "t = 1, 2.3, 'a', {}; checkme = t[:1:-1]" << "tuple of (dict, str)"; 1027 QTest::newRow("tuple_slice_revstop") << "t = 1, 2.3, 'a', {}; checkme = t[2::-1]" << "tuple of (str, float, int)"; 1028 QTest::newRow("tuple_slice_revstop") << "t = 1, 2.3, 'a', {}; checkme = t[::-1]" << "tuple of (dict, str, float, int)"; 1029 QTest::newRow("tuple_slice_no_elems") << "t = 1, 2.3, 'a', {}; checkme = t[1:1]" << "tuple of ()"; 1030 // TODO unsure-tuples. 1031 QTest::newRow("tuple_slice_not_literal") << "n = 2; t = 1, 2.3, 'a', {}; checkme = t[0:n]" << "tuple of ()"; 1032 // These are allowed, for whatever reason. 1033 QTest::newRow("tuple_slice_past_range") << "t = 1, 2.3; checkme = t[-999999999:8888888888]" << "tuple of (int, float)"; 1034 QTest::newRow("tuple_slice_wrong_direction") << "t = 1, 2.3, 'a'; checkme = t[0:3:-1]" << "tuple of ()"; 1035 // This isn't. 1036 QTest::newRow("tuple_slice_zero_step") << "t = 1, 2.3; checkme = t[::0]" << "tuple of ()"; 1037 1038 QTest::newRow("tuple_add") << "t, u = (3,), ('q', 4.5); checkme = t + u" << "tuple of (int, str, float)"; 1039 QTest::newRow("tuple_mul") << "t = 3, 4.5; checkme = t * 2" << "tuple of (int, float, int, float)"; 1040 1041 QTest::newRow("dict_unsure") << "t = dict(); t = {3: str()}\ncheckme = t[1].capitalize()" << "str"; 1042 QTest::newRow("unsure_attr_access") << "d = str(); d = 3; checkme = d.capitalize()" << "str"; 1043 1044 QTest::newRow("class_create_var") << "class c: pass\nd = c()\nd.foo = 3\ncheckme = d.foo" << "int"; 1045 1046 QTest::newRow("tuple_loop") << "t = [(1, \"str\")]\nfor checkme, a in t: pass" << "int"; 1047 1048 QTest::newRow("no_hints_type") << "def myfun(arg): arg = 3; return arg\ncheckme = myfun(3)" << "int"; 1049 QTest::newRow("hints_type") << "def myfun(arg): return arg\ncheckme = myfun(3)" << "int"; 1050 QTest::newRow("args_type") << "def myfun(*args): return args[0]\ncheckme = myfun(3)" << "int"; 1051 QTest::newRow("kwarg_type") << "def myfun(**kwargs): return kwargs['a']\ncheckme = myfun(a=3)" << "int"; 1052 QTest::newRow("dict_kwarg_type") << "def foo(**kwargs): return kwargs['']\ncheckme = foo(**{'a': 12})" << "int"; 1053 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 5, 0) 1054 QTest::newRow("dict_norm_kwarg_type") << "def foo(**kwargs): return kwargs['']\n" 1055 "checkme = foo(**{'a': 12}, b=1.2)" << "unsure (int, float)"; 1056 QTest::newRow("multi_dict_kwarg_type") << "def foo(**kwargs): return kwargs['']\n" 1057 "checkme = foo(**{'a': 12}, b=1.2, **{'c': ''})" << "unsure (int, float, str)"; 1058 #endif 1059 QTest::newRow("named_arg_type") << "def myfun(arg): return arg\ncheckme = myfun(arg=3)" << "int"; 1060 1061 QTest::newRow("arg_args_type") << "def myfun(arg, *args): return args[0]\n" 1062 "checkme = myfun(3, str())" << "str"; 1063 QTest::newRow("arg_kwargs_type") << "def myfun(arg, **kwargs): return kwargs['a']\n" 1064 "checkme = myfun(12, a=str())" << "str"; 1065 QTest::newRow("named_kwargs_type_1") << "def myfun(arg, **kwargs): return arg\n" 1066 "checkme = myfun(arg=12, a=str())" << "int"; 1067 QTest::newRow("named_kwargs_type_2") << "def myfun(arg, **kwargs): return kwargs['a']\n" 1068 "checkme = myfun(arg=12, a=str())" << "str"; 1069 QTest::newRow("kwargs_named_type") << "def myfun(arg, **kwargs): return kwargs['a']\n" 1070 "checkme = myfun(a=str(), arg=12)" << "str"; 1071 QTest::newRow("varied_args_type_1") << "def myfun(arg, *args, **kwargs): return arg\n" 1072 "checkme = myfun(1, 1.5, a=str())" << "int"; 1073 QTest::newRow("varied_args_type_2") << "def myfun(arg, *args, **kwargs): return args[0]\n" 1074 "checkme = myfun(1, 1.5, a=str())" << "float"; 1075 QTest::newRow("varied_args_type_3") << "def myfun(arg, *args, **kwargs): return kwargs['a']\n" 1076 "checkme = myfun(1, 1.5, a=str())" << "str"; 1077 QTest::newRow("nested_arg_name_type") << "def foo(xxx):\n" 1078 " def bar(xxx): return xxx\n" 1079 " return bar('test')\n" 1080 "checkme = foo(10)\n" << "str"; 1081 QTest::newRow("method_args_type_1") << "class MyClass:\n" 1082 " def method(self, arg): return self\n" 1083 "checkme = MyClass().method(12)" << "MyClass"; 1084 QTest::newRow("method_args_type_2") << "class MyClass:\n" 1085 " def method(self, arg): return arg\n" 1086 "checkme = MyClass().method(12)" << "int"; 1087 QTest::newRow("clsmethod_args_type_1") << "class MyClass:\n" 1088 " @classmethod\n" 1089 " def method(cls, arg): return cls\n" 1090 "checkme = MyClass().method(12)" << "MyClass"; 1091 QTest::newRow("clsmethod_args_type_2") << "class MyClass:\n" 1092 " @classmethod\n" 1093 " def method(cls, arg): return arg\n" 1094 "checkme = MyClass().method(12)" << "int"; 1095 QTest::newRow("staticmethod_args_type") << "class MyClass:\n" 1096 " @staticmethod\n" 1097 " def method(arg): return arg\n" 1098 "checkme = MyClass().method(12)" << "int"; 1099 QTest::newRow("staticmethod_vararg_type") << "class MyClass:\n" 1100 " @staticmethod\n" 1101 " def method(arg, *args): return args[0]\n" 1102 "checkme = MyClass().method(12, 2.5)" << "float"; 1103 QTest::newRow("method_explicit_self") << "class MyClass:\n" 1104 " def method(self, arg): return arg\n" 1105 "instance = MyClass()\n" 1106 "checkme = MyClass.method(instance, 12)" << "int"; 1107 QTest::newRow("method_vararg_explicit_self") << "class MyClass:\n" 1108 " def foo(self, arg, *args): return args[0]\n" 1109 "mc = MyClass()\n" 1110 "checkme = MyClass.foo(mc, 'str', 3, 4.5)" << "int"; 1111 QTest::newRow("clsmethod_explicit_self") << "class MyClass:\n" 1112 " @classmethod\n" 1113 " def method(cls, arg1, arg2): return arg2\n" 1114 "instance = MyClass()\n" 1115 "checkme = MyClass.method('a', 12)" << "int"; 1116 QTest::newRow("staticmethod_explicit_self") << "class MyClass:\n" 1117 " @staticmethod\n" 1118 " def method(arg1, arg2): return arg1\n" 1119 "instance = MyClass()\n" 1120 "checkme = MyClass.method('a', 12)" << "str"; 1121 QTest::newRow("parent_constructor_arg_type") << "class Base:\n" // https://bugs.kde.org/show_bug.cgi?id=369364 1122 " def __init__(self, foo):\n" 1123 " self.foo = foo\n" 1124 "class Derived(Base):\n" 1125 " def __init__(self, foo):\n" 1126 " Base.__init__(self, foo)\n" 1127 "instance = Derived('string')\n" 1128 "checkme = instance.foo" << "str"; 1129 QTest::newRow("nested_class_self_inside") << "class Foo:\n" 1130 " def foo(self):\n" 1131 " class Bar:\n" 1132 " def bar(self): return self\n" 1133 " return Bar().bar()\n" 1134 "checkme = Foo().foo()\n" << "Foo::foo::Bar"; 1135 QTest::newRow("nested_class_self_after") << "class Foo:\n" 1136 " class Bar: pass\n" 1137 " def foo(self): return self\n" 1138 "checkme = Foo().foo()\n" << "Foo"; 1139 1140 QTest::newRow("tuple_unsure") << "q = (3, str())\nq=(str(), 3)\ncheckme, _ = q" << "unsure (int, str)"; 1141 1142 QTest::newRow("custom_iterable") << "class Gen2:\n" 1143 " def __iter__(self): return self\n" 1144 " def __next__(self): return 'blah'\n" 1145 "for checkme in Gen2(): pass" << "str"; 1146 QTest::newRow("separate_iterator") << "class Foo:\n" 1147 " def __iter__(self): return Bar()\n" 1148 " def __next__(self): return 'blah'\n" // Not used (or shouldn't be!) 1149 "class Bar:\n" 1150 " def __next__(self): return {1}\n" 1151 "checkme = [a for a in Foo()]" << "list of set of int"; 1152 QTest::newRow("return_builtin_iterator") << "class Gen2:\n" 1153 " contents = [1, 2, 3]\n" 1154 " def __iter__(self): return iter(Gen2.contents)\n" 1155 "for checkme in Gen2(): pass" << "int"; 1156 1157 QTest::newRow("init_class") << "class Foo:\n" 1158 " def __init__(self): pass\n" 1159 " def __call__(self): return 1.5\n" 1160 "checkme = Foo()\n" << "Foo"; 1161 QTest::newRow("init_class_no_decl") << "class Foo:\n" 1162 " def __init__(self): pass\n" 1163 " def __call__(self): return 1.5\n" 1164 "a = [Foo]\n" 1165 "checkme = a[0]()\n" << "Foo"; 1166 QTest::newRow("call_class") << "class Foo:\n" 1167 " def __call__(self):\n" 1168 " return 0\n" 1169 "f = Foo()\n" 1170 "checkme = f()\n" << "int"; 1171 QTest::newRow("call_class_no_decl") << "class Foo:\n" 1172 " def __call__(self): return 1.5\n" 1173 "a = [Foo()]\n" 1174 "checkme = a[0]()" << "float"; 1175 QTest::newRow("classmethod") << "class Foo:\n" 1176 " @classmethod\n" 1177 " def foo(cls):\n" 1178 " k = cls()\n" 1179 " return k\n" 1180 "f = Foo.foo()\n" 1181 "checkme = f\n" << "Foo"; 1182 QTest::newRow("property_getter") << "class Foo:\n" 1183 " @property\n" 1184 " def bar(self): return 35\n" 1185 "checkme = Foo().bar" << "int"; 1186 QTest::newRow("property_wrong") << "class Foo:\n" 1187 " @property\n" 1188 " def bar(self): return True\n" 1189 "checkme = Foo().bar()" << "mixed"; 1190 QTest::newRow("property_setter") << "class Foo:\n" 1191 " @property\n" 1192 " def bar(self): return 35\n" 1193 " @bar.setter\n" 1194 " def bar(self, value): return 18.3\n" // Return should be ignored 1195 "checkme = Foo().bar" << "int"; 1196 1197 QTest::newRow("tuple_listof") << "l = [(1, 2), (3, 4)]\ncheckme = l[1][0]" << "int"; 1198 1199 QTest::newRow("getitem") << "class c:\n def __getitem__(self, slice): return 3.14\na = c()\ncheckme = a[2]" << "float"; 1200 1201 QTest::newRow("constructor_type_deduction") << "class myclass:\n" 1202 "\tdef __init__(self, param): self.foo=param\n" 1203 "checkme = myclass(3).foo" << "int"; 1204 QTest::newRow("simpe_type_deduction") << "def myfunc(arg): return arg\n" 1205 "checkme = myfunc(3)" << "int"; 1206 QTest::newRow("functionCall_functionArg_part1") << "def getstr(): return \"foo\"\n" 1207 "def identity(f): return f\n" 1208 "f1 = getstr\n" 1209 "checkme = f1()" << "str"; 1210 QTest::newRow("functionCall_functionArg_part2") << "def getstr(): return \"foo\"\n" 1211 "def identity(f): return f\n" 1212 "f1 = identity(getstr)\n" 1213 "checkme = f1()\n" << "str"; 1214 QTest::newRow("functionCall_functionArg_full") << "def getstr(): return \"foo\"\n" 1215 "def identity(f): return f\n" 1216 "f1 = getstr\n" 1217 "f2 = identity(getstr)\n" 1218 "a = getstr()\n" 1219 "b = f1()\n" 1220 "checkme = f2()\n" << "str"; 1221 QTest::newRow("vararg_before_other_args") << "def myfun(a, b, *z, x): return z[0]\n" 1222 "checkme = myfun(False, False, 1, x = False)" << "int"; 1223 QTest::newRow("vararg_before_other_args2") << "def myfun(a, b, *z, x): return z[3]\n" 1224 "checkme = myfun(False, False, 1, 2, 3, \"str\", x = False)" << "str"; 1225 QTest::newRow("vararg_constructor") << "class myclass():\n" 1226 " def __init__(self, *arg): self.prop = arg[0]\n" 1227 "obj = myclass(3, 5); checkme = obj.prop" << "int"; 1228 1229 QTest::newRow("declaration_order_var") << "aaa = 2\n" 1230 "checkme = aaa" << "int"; 1231 QTest::newRow("declaration_order_var2") << "checkme = aaa\n" 1232 "aaa = 2" << "mixed"; 1233 QTest::newRow("declaration_order_func_defarg") << "aaa = 2\n" 1234 "def foo(x=aaa): return x\n" 1235 "checkme = foo()" << "int"; 1236 QTest::newRow("declaration_order_func_defarg2") << "def foo(x=aaa): return x\n" 1237 "aaa = 2\n" 1238 "checkme = foo()" << "mixed"; 1239 QTest::newRow("declaration_order_func_body") << "aaa = 2\n" 1240 "def foo(): return aaa\n" 1241 "checkme = foo()" << "int"; 1242 QTest::newRow("declaration_order_func_body2") << "def foo(): return aaa\n" 1243 "aaa = 2\n" 1244 "checkme = foo()" << "int"; 1245 QTest::newRow("global_variable") << "a = 3\n" 1246 "def f1():\n" 1247 " global a\n" 1248 " return a\n" 1249 "checkme = f1()\n" << "int"; 1250 QTest::newRow("global_variable2") << "a = 3\n" 1251 "def f1():\n" 1252 " global a\n" 1253 " a = \"str\"\n" 1254 " return a\n" 1255 "checkme = f1()\n" << "str"; 1256 QTest::newRow("global_scope_variable") << "a = 3\n" 1257 "def f1():\n" 1258 " return a\n" 1259 "checkme = f1()\n" << "int"; 1260 QTest::newRow("global_no_toplevel_dec") << "def f1():\n" 1261 " global a\n a = 3\n" 1262 " return a\n" 1263 "checkme = f1()\n" << "int"; 1264 1265 QTest::newRow("top_level_vs_class_attr") << 1266 "var = 3\n" 1267 "class MyClass:\n" 1268 " var = 'str'\n" 1269 " def f1(): return var\n" 1270 "checkme = MyClass.f1()" << "int"; 1271 QTest::newRow("top_level_vs_instance_attr") << 1272 "var = 3\n" 1273 "class MyClass:\n" 1274 " def __init__(self): self.var = 'str'\n" 1275 " def f1(): return var\n" 1276 "checkme = MyClass.f1()" << "int"; 1277 QTest::newRow("intermediate_vs_class/instance_attrs") << 1278 "def func():\n" 1279 " aa, bb = 3, 4\n" 1280 " class Foo:\n" 1281 " aa = 'a'\n" 1282 " def __init__(self):\n" 1283 " self.bb = 'b'\n" 1284 " def foo(self):\n" 1285 " return aa, bb\n" 1286 " return Foo().foo()\n" 1287 "checkme = func()" << "tuple of (int, int)"; 1288 QTest::newRow("top_level_vs_nested_class_attrs") << 1289 "aaa = 'foo'\n" 1290 "bbb = 'bar'\n" 1291 "class Foo:\n" 1292 " aaa = 1\n" 1293 " class Bar:\n" 1294 " bbb = 2\n" 1295 " def foo(self, ccc=aaa, ddd=bbb):\n" // Bar.bbb is visible here, Foo.aaa isn't. 1296 " return ccc, ddd\n" 1297 "checkme = Foo().Bar().foo()\n" << "tuple of (str, int)"; 1298 QTest::newRow("top_level_vs_nested_instance_attrs") << 1299 "aaa = 'foo'\n" 1300 "bbb = 'bar'\n" 1301 "class Foo:\n" 1302 " def __init__(self): self.aaa = 1\n" 1303 " class Bar:\n" 1304 " def __init__(self): self.bbb = 1\n" 1305 " def foo(self, ccc=aaa, ddd=bbb):\n" // self.bbb is visible here, Foo().aaa isn't. 1306 " return ccc, ddd\n" 1307 "checkme = Foo().Bar().foo()\n" << "tuple of (str, int)"; 1308 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 8, 0) 1309 QTest::newRow("assignment_expr_while") << 1310 "file = open('foo.txt')\n" 1311 "while q := file.readline():\n" 1312 " checkme = q\n" << "str"; 1313 QTest::newRow("assignment_expr_comprehension") << 1314 "checkme = [z for q in (1, 2, 3) if (z := q % 2)]" << "list of int"; 1315 QTest::newRow("assignment_expr_context") << 1316 "a = [z for q in (1, 2, 3) if (z := q % 2)]\n" 1317 "checkme = z" << "int"; 1318 QTest::newRow("positional_params") << 1319 "def foo(a, b, /, c, d):\n" 1320 " return a, b, c, d\n" 1321 "checkme = foo(10, 'x', 2.3, d='y')\n" << "tuple of (int, str, float, str)"; 1322 QTest::newRow("fstring_self_documenting") << "checkme = f'{expr=}'" << "str"; 1323 #endif 1324 } 1325 1326 typedef QPair<Declaration*, int> pair; 1327 1328 void PyDUChainTest::testImportDeclarations() { 1329 QFETCH(QString, code); 1330 QFETCH(QStringList, expectedDecls); 1331 QFETCH(bool, shouldBeAliased); 1332 1333 ReferencedTopDUContext ctx = parse(code.toUtf8()); 1334 QVERIFY(ctx); 1335 QVERIFY(m_ast); 1336 1337 DUChainReadLocker lock(DUChain::lock()); 1338 foreach ( const QString& expected, expectedDecls ) { 1339 bool found = false; 1340 QString name = expected; 1341 const auto decls = ctx->allDeclarations(CursorInRevision::invalid(), ctx->topContext(), false); 1342 qCDebug(KDEV_PYTHON_DUCHAIN) << "FOUND DECLARATIONS:"; 1343 foreach ( const pair& current, decls ) { 1344 qCDebug(KDEV_PYTHON_DUCHAIN) << current.first->toString() << current.first->identifier().identifier().byteArray() << name; 1345 } 1346 foreach ( const pair& current, decls ) { 1347 if ( ! ( current.first->identifier().identifier().byteArray() == name ) ) continue; 1348 qCDebug(KDEV_PYTHON_DUCHAIN) << "Found: " << current.first->toString() << " for " << name; 1349 AliasDeclaration* isAliased = dynamic_cast<AliasDeclaration*>(current.first); 1350 if ( isAliased && shouldBeAliased ) { 1351 found = true; // TODO fixme 1352 } 1353 else if ( ! isAliased && ! shouldBeAliased ) { 1354 found = true; 1355 } 1356 } 1357 QVERIFY(found); 1358 } 1359 } 1360 1361 void PyDUChainTest::testProblemCount() 1362 { 1363 QFETCH(QString, code); 1364 QFETCH(int, problemsCount); 1365 1366 ReferencedTopDUContext ctx = parse(code); 1367 QVERIFY(ctx); 1368 1369 DUChainReadLocker lock; 1370 QEXPECT_FAIL("fstring_visit_inside", "Ranges are broken so we don't visit the expression", Continue); 1371 QCOMPARE(ctx->problems().size(), problemsCount); 1372 } 1373 1374 void PyDUChainTest::testProblemCount_data() 1375 { 1376 QTest::addColumn<QString>("code"); 1377 QTest::addColumn<int>("problemsCount"); 1378 1379 QTest::newRow("list_comp") << "[foo for foo in range(3)]" << 0; 1380 QTest::newRow("list_comp_wrong") << "[bar for foo in range(3)]" << 1; 1381 QTest::newRow("list_comp_staticmethod") << "class A:\n @staticmethod\n def func(cls):\n" 1382 " [a for a in [1, 2, 3]]" << 0; 1383 QTest::newRow("list_comp_other_decorator") << "def decorate(): pass\nclass A:\n @decorate\n def func(self):\n" 1384 " [a for a in [1, 2, 3]]" << 0; 1385 QTest::newRow("list_comp_other_wrong") << "def decorate(): pass\nclass A:\n @decorate\n def func(self):\n" 1386 " [x for a in [1, 2, 3]]" << 1; 1387 QTest::newRow("list_comp_staticmethod_wrong") << "class A:\n @staticmethod\n def func(cls):\n" 1388 " [x for a in [1, 2, 3]]" << 1; 1389 QTest::newRow("misplaced_return_plain") << "return" << 1; 1390 QTest::newRow("misplaced_return_value") << "return 15" << 1; 1391 QTest::newRow("misplaced_return_class") << "class A:\n return 25" << 1; 1392 QTest::newRow("correct_return") << "def foo():\n return" << 0; 1393 QTest::newRow("lambda_argument_outside") << "def bar():\n lambda foo: 3\n foo" << 1; 1394 QTest::newRow("use_found_at_decl") << "foo = 3" << 0; 1395 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 6, 0) 1396 QTest::newRow("fstring_visit_inside") << "checkme = f'{name}'" << 1; 1397 #endif 1398 } 1399 1400 void PyDUChainTest::testImportDeclarations_data() { 1401 QTest::addColumn<QString>("code"); 1402 QTest::addColumn<QStringList>("expectedDecls"); 1403 QTest::addColumn<bool>("shouldBeAliased"); 1404 1405 QTest::newRow("from_import") << "from testImportDeclarations.i import checkme" << ( QStringList() << "checkme" ) << true; 1406 QTest::newRow("import") << "import testImportDeclarations.i" << ( QStringList() << "testImportDeclarations" ) << false; 1407 QTest::newRow("import_as") << "import testImportDeclarations.i as checkme" << ( QStringList() << "checkme" ) << false; 1408 QTest::newRow("from_import_as") << "from testImportDeclarations.i import checkme as checkme" << ( QStringList() << "checkme" ) << true; 1409 QTest::newRow("from_import_missing") << "from testImportDeclarations.i import missing as checkme" << ( QStringList() ) << true; 1410 } 1411 1412 typedef QPair<Declaration*, int> p; 1413 1414 void PyDUChainTest::testAutocompletionFlickering() 1415 { 1416 TestFile f("foo = 3\nfoo2 = 2\nfo", "py"); 1417 f.parse(TopDUContext::ForceUpdate); 1418 f.waitForParsed(500); 1419 1420 ReferencedTopDUContext ctx1 = f.topContext(); 1421 DUChainWriteLocker lock(DUChain::lock()); 1422 QVERIFY(ctx1); 1423 auto decls1 = ctx1->allDeclarations(CursorInRevision::invalid(), ctx1->topContext()); 1424 QList<DeclarationId> declIds; 1425 foreach ( p d, decls1 ) { 1426 declIds << d.first->id(); 1427 } 1428 lock.unlock(); 1429 1430 f.setFileContents("foo = 3\nfoo2 = 2\nfoo"); 1431 f.parse(TopDUContext::ForceUpdate); 1432 f.waitForParsed(500); 1433 1434 ReferencedTopDUContext ctx2 = f.topContext(); 1435 QVERIFY(ctx2); 1436 lock.lock(); 1437 auto decls2 = ctx2->allDeclarations(CursorInRevision::invalid(), ctx2->topContext()); 1438 foreach ( p d2, decls2 ) { 1439 qCDebug(KDEV_PYTHON_DUCHAIN) << "@1: " << d2.first->toString() << "::" << d2.first->id().hash() << "<>" << declIds.first().hash(); 1440 QVERIFY(d2.first->id() == declIds.first()); 1441 declIds.removeFirst(); 1442 } 1443 lock.unlock(); 1444 1445 qDebug() << "========================="; 1446 1447 TestFile g("def func():\n\tfoo = 3\n\tfoo2 = 2\n\tfo", "py"); 1448 g.parse(TopDUContext::ForceUpdate); 1449 g.waitForParsed(500); 1450 1451 ctx1 = g.topContext(); 1452 lock.lock(); 1453 QVERIFY(ctx1); 1454 decls1 = ctx1->allDeclarations(CursorInRevision::invalid(), ctx1->topContext(), false).first().first->internalContext() 1455 ->allDeclarations(CursorInRevision::invalid(), ctx1->topContext()); 1456 declIds.clear(); 1457 foreach ( p d, decls1 ) { 1458 declIds << d.first->id(); 1459 } 1460 lock.unlock(); 1461 1462 g.setFileContents("def func():\n\tfoo = 3\n\tfoo2 = 2\n\tfoo"); 1463 g.parse(TopDUContext::ForceUpdate); 1464 g.waitForParsed(500); 1465 1466 ctx2 = g.topContext(); 1467 QVERIFY(ctx2); 1468 lock.lock(); 1469 decls2 = ctx2->allDeclarations(CursorInRevision::invalid(), ctx2->topContext(), false).first().first->internalContext() 1470 ->allDeclarations(CursorInRevision::invalid(), ctx2->topContext()); 1471 foreach ( p d2, decls2 ) { 1472 qCDebug(KDEV_PYTHON_DUCHAIN) << "@2: " << d2.first->toString() << "::" << d2.first->id().hash() << "<>" << declIds.first().hash(); 1473 QVERIFY(d2.first->id() == declIds.first()); 1474 declIds.removeFirst(); 1475 } 1476 lock.unlock(); 1477 } 1478 1479 void PyDUChainTest::testFunctionHints() 1480 { 1481 QFETCH(QString, code); 1482 QFETCH(QString, expectedType); 1483 ReferencedTopDUContext ctx = parse(code); 1484 QVERIFY(ctx); 1485 DUChainWriteLocker lock; 1486 QList< Declaration* > decls = ctx->findDeclarations(KDevelop::Identifier("checkme")); 1487 QVERIFY(! decls.isEmpty()); 1488 Declaration* d = decls.first(); 1489 QVERIFY(d->abstractType()); 1490 QCOMPARE(d->abstractType()->toString(), expectedType); 1491 } 1492 1493 void PyDUChainTest::testFunctionHints_data() 1494 { 1495 QTest::addColumn<QString>("code"); 1496 QTest::addColumn<QString>("expectedType"); 1497 1498 QTest::newRow("func_return_type") << "def myfun(arg) -> int: pass\ncheckme = myfun(\"3\")" << "unsure (None, int)"; 1499 QTest::newRow("argument_type") << "def myfun(arg : int): return arg\ncheckme = myfun(foobar)" << "int"; 1500 QTest::newRow("argument_type_only_if_typeof") << "def myfun(arg : 3): return arg\ncheckme = myfun(foobar)" << "mixed"; 1501 } 1502 1503 void PyDUChainTest::testHintedTypes() 1504 { 1505 QFETCH(QString, code); 1506 QFETCH(QString, expectedType); 1507 ReferencedTopDUContext ctx = parse(code); 1508 QVERIFY(ctx); 1509 DUChainWriteLocker lock; 1510 QList< Declaration* > decls = ctx->findDeclarations(KDevelop::Identifier("checkme")); 1511 QVERIFY(! decls.isEmpty()); 1512 Declaration* d = decls.first(); 1513 QVERIFY(d->abstractType()); 1514 QCOMPARE(d->abstractType()->toString(), expectedType); 1515 } 1516 1517 void PyDUChainTest::testHintedTypes_data() 1518 { 1519 QTest::addColumn<QString>("code"); 1520 QTest::addColumn<QString>("expectedType"); 1521 1522 QTest::newRow("simple_hint") << "def myfunc(x): return x\ncheckme = myfunc(3)" << "int"; 1523 QTest::newRow("hint_unsure") << "def myfunc(x): return x\nmyfunc(3.5)\ncheckme = myfunc(3)" << "unsure (float, int)"; 1524 QTest::newRow("unsure_attribute") << "def myfunc(x): return x.capitalize()\nmyfunc(3.5)\ncheckme = myfunc(str())" << "str"; 1525 } 1526 1527 void PyDUChainTest::testOperators() 1528 { 1529 QFETCH(QString, code); 1530 QFETCH(QString, expectedType); 1531 code.prepend("from testOperators.example import *\n\n"); 1532 ReferencedTopDUContext ctx = parse(code); 1533 QVERIFY(ctx); 1534 1535 DUChainReadLocker lock(DUChain::lock()); 1536 TypeTestVisitor* visitor = new TypeTestVisitor(); 1537 visitor->ctx = TopDUContextPointer(ctx.data()); 1538 visitor->searchingForType = expectedType; 1539 visitor->visitCode(m_ast.data()); 1540 1541 QVERIFY(visitor->found); 1542 } 1543 1544 void PyDUChainTest::testOperators_data() 1545 { 1546 QTest::addColumn<QString>("code"); 1547 QTest::addColumn<QString>("expectedType"); 1548 1549 QTest::newRow("add") << "checkme = Example() + Example()" << "Add"; 1550 QTest::newRow("sub") << "checkme = Example() - Example()" << "Sub"; 1551 QTest::newRow("mul") << "checkme = Example() * Example()" << "Mul"; 1552 QTest::newRow("floordiv") << "checkme = Example() // Example()" << "Floordiv"; 1553 QTest::newRow("mod") << "checkme = Example() % Example()" << "Mod"; 1554 QTest::newRow("pow") << "checkme = Example() ** Example()" << "Pow"; 1555 QTest::newRow("lshift") << "checkme = Example() << Example()" << "Lshift"; 1556 QTest::newRow("rshift") << "checkme = Example() >> Example()" << "Rshift"; 1557 QTest::newRow("and") << "checkme = Example() & Example()" << "And"; 1558 QTest::newRow("xor") << "checkme = Example() ^ Example()" << "Xor"; 1559 QTest::newRow("or") << "checkme = Example() | Example()" << "Or"; 1560 } 1561 1562 void PyDUChainTest::testFunctionArgs() 1563 { 1564 ReferencedTopDUContext ctx = parse("def ASDF(arg1, arg2):\n" 1565 " arg1 = arg2"); 1566 DUChainWriteLocker lock(DUChain::lock()); 1567 QVERIFY(ctx); 1568 QVERIFY(m_ast); 1569 // dumpDUContext(ctx); 1570 1571 QCOMPARE(ctx->childContexts().size(), 2); 1572 DUContext* funcArgCtx = ctx->childContexts().first(); 1573 QCOMPARE(funcArgCtx->type(), DUContext::Function); 1574 QCOMPARE(funcArgCtx->localDeclarations().size(), 2); 1575 QVERIFY(!funcArgCtx->owner()); 1576 1577 Python::FunctionDeclaration* decl = dynamic_cast<Python::FunctionDeclaration*>( 1578 ctx->allDeclarations(CursorInRevision::invalid(), ctx->topContext()).first().first); 1579 QVERIFY(decl); 1580 QCOMPARE(decl->type<FunctionType>()->arguments().length(), 2); 1581 qDebug() << decl->type<FunctionType>()->arguments().length() << 2; 1582 1583 DUContext* funcBodyCtx = ctx->childContexts().last(); 1584 QCOMPARE(funcBodyCtx->type(), DUContext::Other); 1585 QVERIFY(funcBodyCtx->owner()); 1586 QVERIFY(funcBodyCtx->localDeclarations().isEmpty()); 1587 } 1588 1589 void PyDUChainTest::testInheritance() 1590 { 1591 QFETCH(QString, code); 1592 QFETCH(int, expectedBaseClasses); 1593 ReferencedTopDUContext ctx = parse(code); 1594 QVERIFY(ctx); 1595 DUChainReadLocker lock(DUChain::lock()); 1596 auto decls = ctx->allDeclarations(CursorInRevision::invalid(), ctx->topContext(), false); 1597 bool found = false; 1598 bool classDeclFound = false; 1599 foreach ( const p& item, decls ) { 1600 if ( item.first->identifier().toString() == "B" ) { 1601 auto klass = dynamic_cast<ClassDeclaration*>(item.first); 1602 QVERIFY(klass); 1603 QCOMPARE(klass->baseClassesSize(), static_cast<unsigned int>(expectedBaseClasses)); 1604 classDeclFound = true; 1605 } 1606 if ( item.first->identifier().toString() == "checkme" ) { 1607 QCOMPARE(item.first->abstractType()->toString(), QString("int")); 1608 found = true; 1609 } 1610 } 1611 QVERIFY(found); 1612 QVERIFY(classDeclFound); 1613 } 1614 1615 void PyDUChainTest::testInheritance_data() 1616 { 1617 QTest::addColumn<QString>("code"); 1618 QTest::addColumn<int>("expectedBaseClasses"); 1619 1620 QTest::newRow("simple") << "class A():\n\tattr = 3\n\nclass B(A):\n\tpass\n\ninst=B()\ncheckme = inst.attr" << 1; 1621 QTest::newRow("context_import_prereq") << "import testInheritance.i\ninst=testInheritance.i.testclass()\n" 1622 "checkme = inst.attr\nclass B(): pass" << 1; // 1 because object 1623 QTest::newRow("context_import") << "import testInheritance.i\n\nclass B(testInheritance.i.testclass):\n" 1624 "\ti = 4\n\ninst=B()\ncheckme = inst.attr" << 1; 1625 } 1626 1627 void PyDUChainTest::testClassContextRanges() 1628 { 1629 QString code = "class my_class():\n pass\n \n \n \n \n"; 1630 ReferencedTopDUContext ctx = parse(code); 1631 DUChainWriteLocker lock; 1632 DUContext* classContext = ctx->findContextAt(CursorInRevision(5, 0)); 1633 QVERIFY(classContext); 1634 QVERIFY(classContext->type() == DUContext::Class); 1635 } 1636 1637 void PyDUChainTest::testContainerTypes() 1638 { 1639 QFETCH(QString, code); 1640 QFETCH(QString, contenttype); 1641 QFETCH(bool, use_type); 1642 ReferencedTopDUContext ctx = parse(code); 1643 QVERIFY(ctx); 1644 1645 DUChainReadLocker lock(DUChain::lock()); 1646 QList<Declaration*> decls = ctx->findDeclarations(QualifiedIdentifier("checkme")); 1647 QVERIFY(decls.length() > 0); 1648 QVERIFY(decls.first()->abstractType()); 1649 if ( ! use_type ) { 1650 auto type = decls.first()->abstractType().dynamicCast<ListType>(); 1651 QVERIFY(type); 1652 QVERIFY(type->contentType()); 1653 QCOMPARE(type->contentType().abstractType()->toString(), contenttype); 1654 } 1655 else { 1656 QVERIFY(decls.first()->abstractType()); 1657 QEXPECT_FAIL("dict_of_int_call", "returnContentEqualsContentOf isn't suitable", Continue); 1658 QEXPECT_FAIL("dict_from_tuples", "returnContentEqualsContentOf isn't suitable", Continue); 1659 QEXPECT_FAIL("comprehension_shadowing_ms", "Nothing is foolproof to a sufficiently capable fool", Continue); 1660 QEXPECT_FAIL("comprehension_shadowing_nest2", "See above", Continue); 1661 QCOMPARE(decls.first()->abstractType()->toString(), contenttype); 1662 } 1663 } 1664 1665 void PyDUChainTest::testContainerTypes_data() 1666 { 1667 QTest::addColumn<QString>("code"); 1668 QTest::addColumn<QString>("contenttype"); 1669 QTest::addColumn<bool>("use_type"); 1670 1671 QTest::newRow("list_of_int") << "checkme = [1, 2, 3]" << "int" << false; 1672 QTest::newRow("list_from_unpacked") << "foo = [1.3]\ncheckme = [1, *foo, 3]" << "unsure (int, float)" << false; 1673 QTest::newRow("list_of_int_call") << "checkme = list([1, 2, 3])" << "int" << false; 1674 QTest::newRow("list_from_tuple") << "checkme = list((1, 2, 3))" << "int" << false; 1675 QTest::newRow("list_from_dict") << "checkme = list({'a':1, 'b':2})" << "str" << false; // Gets key type! 1676 QTest::newRow("list_from_custom_iter") << 1677 "class MyClass:\n" 1678 " def __iter__(self): return self\n" 1679 " def __next__(self): return 3.1417\n" 1680 "checkme = list(MyClass())" << "float" << false; 1681 QTest::newRow("generator") << "checkme = [i for i in [1, 2, 3]]" << "int" << false; 1682 QTest::newRow("list_access") << "list = [1, 2, 3]\ncheckme = list[0]" << "int" << true; 1683 QTest::newRow("set_of_int") << "checkme = {1, 2, 3}" << "int" << false; 1684 QTest::newRow("set_of_int_call") << "checkme = set({1, 2, 3})" << "int" << false; 1685 QTest::newRow("set_from_tuple") << "checkme = set((1, 2, 3))" << "int" << false; 1686 QTest::newRow("set_generator") << "checkme = {i for i in [1, 2, 3]}" << "int" << false; 1687 QTest::newRow("dict_of_str_int") << "checkme = {'a':1, 'b':2, 'c':3}" << "dict of str : int" << true; 1688 QTest::newRow("frozenset_of_int_call") << "checkme = frozenset({1, 2, 3})" << "int" << false; 1689 QTest::newRow("dict_of_int") << "checkme = {a:1, b:2, c:3}" << "int" << false; 1690 QTest::newRow("dict_of_int_call") << "checkme = dict({'a':1, 'b':2, 'c':3})" << "dict of str : int" << true; 1691 QTest::newRow("dict_from_tuples") << "checkme = dict([('a', 1), ('b', 2)])" << "dict of str : int" << true; 1692 QTest::newRow("dict_generator") << "checkme = {\"Foo\":i for i in [1, 2, 3]}" << "int" << false; 1693 QTest::newRow("dict_access") << "list = {'a':1, 'b':2, 'c':3}\ncheckme = list[0]" << "int" << true; 1694 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 5, 0) 1695 QTest::newRow("set_from_unpacked") << "foo = [1.3]\ncheckme = {1, *foo, 3}" << "unsure (int, float)" << false; 1696 QTest::newRow("dict_from_unpacked") << "checkme = {**{'a': 1}}" << "dict of str : int" << true; 1697 QTest::newRow("dict_from_varied") << "checkme = {**{'a': 1}, 1: 1.5}" << 1698 "dict of unsure (str, int) : unsure (int, float)" << true; 1699 #endif 1700 QTest::newRow("generator_attribute") << "checkme = [item.capitalize() for item in ['foobar']]" << "str" << false; 1701 QTest::newRow("cannot_change_type") << "checkme = [\"Foo\", \"Bar\"]" << "str" << false; 1702 QTest::newRow("cannot_change_type2") << "[1, 2, 3].append(5)\ncheckme = [\"Foo\", \"Bar\"]" << "str" << false; 1703 1704 QTest::newRow("list_append") << "d = []\nd.append(3)\ncheckme = d[0]" << "int" << true; 1705 QTest::newRow("list_extend") << "d = []; q = [int()]\nd.extend(q)\ncheckme = d[0]" << "int" << true; 1706 QTest::newRow("list_extend_with_tuple") << "d = []; q = (1, 2)\nd.extend(q)\ncheckme = d[0]" << "int" << true; 1707 QTest::newRow("list_extend_with_custom_iter") << 1708 "class MyClass:\n" 1709 " def __iter__(self): return self\n" 1710 " def __next__(self): return 3.1417\n" 1711 "checkme = []\ncheckme.extend(MyClass())" << "float" << false; 1712 1713 QTest::newRow("for_loop") << "d = [3]\nfor item in d:\n checkme = item" << "int" << true; 1714 QTest::newRow("for_loop_unsure") << "d = [3, \"foo\"]\nfor item in d:\n checkme = item" << "unsure (int, str)" << true; 1715 QTest::newRow("for_loop_tuple_1") << "d = [(3, 3.5)]\nfor a, b in d:\n checkme = a" << "int" << true; 1716 QTest::newRow("for_loop_tuple_2") << "d = [(3, 3.5)]\nfor a, b in d:\n checkme = b" << "float" << true; 1717 QTest::newRow("for_loop_tuple_unsure") << "d = [(3, 3.5), (3.5, 3)]\nfor a, b in d:\n checkme = b" 1718 << "unsure (float, int)" << true; 1719 // Proposed by Nicolás Alvarez; why not? https://bugs.kde.org/show_bug.cgi?id=359915 1720 QTest::newRow("comprehension_messy") << "users = {'a':19, 'b':42, 'c':35}\n" 1721 "sorted_list = sorted(users.items(), key=lambda kv: (-kv[1], kv[0]))\n" 1722 "checkme = [k for r,(k,v) in enumerate(sorted_list, 1)]" << "list of str" << true; 1723 QTest::newRow("comprehension_multiline") << "checkme = [a for\n a in \n (1, 2)]" << "list of int" << true; 1724 QTest::newRow("comprehension_multistage") << "nested = (1, 2), (3, 4)\n" 1725 "checkme = [foo for bar in nested for foo in bar]" << "list of int" << true; 1726 QTest::newRow("comprehension_shadowing_ms") << "nested = (1, 2), (3, 4)\n" 1727 "checkme = [foo for foo in nested for foo in foo]" << "list of int" << true; 1728 QTest::newRow("comprehension_shadowing_nest1") << "nested = (1, 2), (3, 4)\n" 1729 "checkme = [foo for foo in [foo for foo in nested]]" << "list of tuple of (int, int)" << true; 1730 QTest::newRow("comprehension_shadowing_nest2") << "nested = (1, 2), (3, 4)\n" 1731 "checkme = [[foo for foo in foo] for foo in nested]" << "list of list of int" << true; 1732 // From https://bugs.kde.org/show_bug.cgi?id=359912 1733 QTest::newRow("subscript_multi") << 1734 "class Middle:\n def __getitem__(self, key):\n return str()\n" 1735 "class Outer:\n def __getitem__(self, key):\n return Middle()\n" 1736 "aaa = Outer()\ncheckme = aaa[0][0]" << "str" << true; 1737 QTest::newRow("subscript_func_call") << 1738 "class Foo:\n def __getitem__(self, key):\n return str()\n" 1739 "def bar():\n return Foo()\n" 1740 "checkme = bar()[0]" << "str" << true; 1741 QTest::newRow("subscript_unknown_index") << "a = 1,str()\ncheckme = a[5-4]" << "unsure (int, str)" << true; 1742 QTest::newRow("subscript_unsure") << "a = 1,2\na=[str()]\ncheckme = a[0]" << "unsure (int, str)" << true; 1743 QTest::newRow("subscript_unsure_getitem") << 1744 "class Foo:\n def __getitem__(self, key):\n return str()\n" 1745 "class Bar:\n def __getitem__(self, key):\n return float()\n" 1746 "a = Foo()\na=Bar()\na=[1,2]\ncheckme = a[1]" << "unsure (str, float, int)" << true; 1747 } 1748 1749 void PyDUChainTest::testVariableCreation() 1750 { 1751 QFETCH(QString, code); 1752 QFETCH(QStringList, expected_local_declarations); 1753 QFETCH(QStringList, expected_types); 1754 1755 ReferencedTopDUContext top = parse(code); 1756 QVERIFY(top); 1757 1758 DUChainReadLocker lock; 1759 auto localDecls = top->localDeclarations(); 1760 QVector<QString> localDeclNames; 1761 for ( const Declaration* d: localDecls ) { 1762 localDeclNames.append(d->identifier().toString()); 1763 } 1764 Q_ASSERT(expected_types.size() == expected_local_declarations.size()); 1765 int offset = 0; 1766 for ( const QString& expected : expected_local_declarations ) { 1767 int index = localDeclNames.indexOf(expected); 1768 QVERIFY(index != -1); 1769 QVERIFY(localDecls[index]->abstractType()); 1770 QCOMPARE(localDecls[index]->abstractType()->toString(), expected_types[offset]); 1771 offset++; 1772 } 1773 } 1774 1775 void PyDUChainTest::testVariableCreation_data() 1776 { 1777 QTest::addColumn<QString>("code"); 1778 QTest::addColumn<QStringList>("expected_local_declarations"); 1779 QTest::addColumn<QStringList>("expected_types"); 1780 1781 QTest::newRow("simple") << "a = 3" << QStringList{"a"} << QStringList{"int"}; 1782 QTest::newRow("tuple_wrong") << "a, b = 3" << QStringList{"a", "b"} << QStringList{"mixed", "mixed"}; 1783 QTest::newRow("tuple_unpack_inplace") << "a, b = 3, 5.5" << QStringList{"a", "b"} << QStringList{"int", "float"}; 1784 QTest::newRow("tuple_unpack_indirect") << "c = 3, 3.5\na, b = c" << QStringList{"a", "b"} << QStringList{"int", "float"}; 1785 QTest::newRow("tuple_unpack_stacked_inplace") << "a, (b, c) = 1, (2, 3.5)" << QStringList{"a", "b", "c"} 1786 << QStringList{"int", "int", "float"}; 1787 QTest::newRow("tuple_unpack_stacked_indirect") << "d = 3.5, (3, 1)\na, (b, c) = d" 1788 << QStringList{"a", "b", "c"} << QStringList{"float", "int", "int"}; 1789 QTest::newRow("unpack_from_list_inplace") << "a, b = [1, 2, 3]" << QStringList{"a", "b"} << QStringList{"int", "int"}; 1790 QTest::newRow("unpack_from_list_indirect") << "c = [1, 2, 3]\na, b = c" << QStringList{"a", "b"} 1791 << QStringList{"int", "int"}; 1792 QTest::newRow("unpack_custom_iterable") << 1793 "class Foo:\n" 1794 " def __iter__(self): return self\n" 1795 " def __next__(self): return 1.5\n" 1796 "a, *b = Foo()" << QStringList{"a", "b"} << QStringList {"float", "list of float"}; 1797 QTest::newRow("for_loop_simple") << "for i in range(3): pass" << QStringList{"i"} << QStringList{"int"}; 1798 QTest::newRow("for_loop_unpack") << "for a, b in [(3, 5.1)]: pass" << QStringList{"a", "b"} 1799 << QStringList{"int", "float"}; 1800 QTest::newRow("for_loop_stacked") << "for a, (b, c) in [(1, (2, 3.5))]: pass" << QStringList{"a", "b", "c"} 1801 << QStringList{"int", "int", "float"}; 1802 QTest::newRow("for_loop_tuple") << "for a in 1, 2: pass" << QStringList{"a"} << QStringList{"int"}; 1803 QTest::newRow("for_loop_dict") << "for a in {'foo': 1}: pass" << QStringList{"a"} << QStringList{"str"}; 1804 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 8, 0) 1805 QTest::newRow("assignment_expr") << "a = (b := 10)" << QStringList{"a", "b"} << QStringList{"int", "int"}; 1806 #endif 1807 #if PYTHON_VERSION >= QT_VERSION_CHECK(3, 10, 0) 1808 QTest::newRow("match") << "match 'x'.split():\n case [a, b]: pass" << QStringList{"a", "b"} << QStringList{"str", "str"}; 1809 QTest::newRow("match_as") << "match 'x'.split():\n case [a, b] as w: pass" << QStringList{"w"} << QStringList{"list of str"}; 1810 #endif 1811 } 1812 1813 void PyDUChainTest::testCleanupMultiplePasses() 1814 { 1815 for ( int j = 0; j < 20; j++ ) { 1816 ReferencedTopDUContext top = parse("from testCleanupMultiplePasses import foo\ndef fonc(): return 3+2j\nfoo.foo.func = fonc"); 1817 } 1818 } 1819 1820 void PyDUChainTest::testManyDeclarations() 1821 { 1822 ReferencedTopDUContext top = parse("from testManyDeclarations import test\nk=test.Foo()"); 1823 } 1824 1825 void PyDUChainTest::testComments() 1826 { 1827 QFETCH(QString, code); 1828 1829 auto top = parse(code); 1830 QVERIFY(top); 1831 DUChainReadLocker lock; 1832 1833 auto decls = top->findDeclarations(QualifiedIdentifier("a")); 1834 QCOMPARE(decls.size(), 1); 1835 auto a = decls.first(); 1836 QCOMPARE(a->comment(), QByteArray("comment")); 1837 1838 decls = top->findDeclarations(QualifiedIdentifier("b")); 1839 if ( decls.isEmpty() ) { 1840 decls = top->childContexts().last()->findDeclarations(QualifiedIdentifier("b")); 1841 } 1842 auto b = decls.first(); 1843 QCOMPARE(b->comment(), QByteArray()); 1844 } 1845 1846 void PyDUChainTest::testComments_data() 1847 { 1848 QTest::addColumn<QString>("code"); 1849 1850 QTest::newRow("variable") << "b=5\n\"\"\"comment\"\"\"\na=5\nb=5"; 1851 QTest::newRow("function") << "def a():\n \"\"\"comment\"\"\"\n b=5"; 1852 QTest::newRow("class") << "class a:\n \"\"\"comment\"\"\"\n b=5"; 1853 } 1854 1855 #include "moc_pyduchaintest.cpp"