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"