helpers.cpp 22.1 KB
Newer Older
1 2
/*****************************************************************************
 * This file is part of KDevelop                                             *
3
 * Copyright 2011-2013 Sven Brauch <svenbrauch@googlemail.com>               *
4
 *                                                                           *
5 6 7 8
 * This program is free software; you can redistribute it and/or             *
 * modify it under the terms of the GNU General Public License as            *
 * published by the Free Software Foundation; either version 2 of            *
 * the License, or (at your option) any later version.                       *
9
 *                                                                           *
10 11 12 13
 * This program is distributed in the hope that it will be useful,           *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 * GNU General Public License for more details.                              *
14
 *                                                                           *
15 16 17 18
 * You should have received a copy of the GNU General Public License         *
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.     *
 *****************************************************************************
 */
19

Sven Brauch's avatar
Sven Brauch committed
20 21 22
#include "helpers.h"

#include <QList>
23
#include <QProcess>
24
#include <QSettings>
25
#include <QStandardPaths>
Sven Brauch's avatar
Sven Brauch committed
26

27 28 29
#include <QDebug>
#include "duchaindebug.h"

30
#include <language/duchain/types/unsuretype.h>
31
#include <language/duchain/types/integraltype.h>
32
#include <language/duchain/types/containertypes.h>
33
#include <language/duchain/types/functiontype.h>
34
#include <language/duchain/duchainutils.h>
Sven Brauch's avatar
Sven Brauch committed
35 36
#include <language/duchain/duchainlock.h>
#include <language/duchain/duchain.h>
37
#include <language/duchain/classdeclaration.h>
38
#include <language/duchain/aliasdeclaration.h>
39
#include <language/duchain/types/typeutils.h>
40
#include <language/backgroundparser/backgroundparser.h>
Sven Brauch's avatar
Sven Brauch committed
41
#include <interfaces/iproject.h>
42
#include <interfaces/iprojectcontroller.h>
Sven Brauch's avatar
Sven Brauch committed
43
#include <interfaces/icore.h>
44
#include <interfaces/ilanguagecontroller.h>
45
#include <interfaces/idocumentcontroller.h>
Sven Brauch's avatar
Sven Brauch committed
46
#include <interfaces/ipartcontroller.h>
47
#include <util/path.h>
Sven Brauch's avatar
Sven Brauch committed
48 49

#include <shell/partcontroller.h>
50 51

#include <KTextEditor/View>
52
#include <KConfigGroup>
Sven Brauch's avatar
Sven Brauch committed
53 54

#include "ast.h"
55
#include "types/hintedtype.h"
Sven Brauch's avatar
Sven Brauch committed
56
#include "types/unsuretype.h"
Sven Brauch's avatar
Sven Brauch committed
57
#include "types/indexedcontainer.h"
58
#include "kdevpythonversion.h"
59
#include "expressionvisitor.h"
Sven Brauch's avatar
Sven Brauch committed
60 61 62 63

using namespace KDevelop;

namespace Python {
64

65 66 67
QMap<IProject*, QVector<QUrl>> Helper::cachedCustomIncludes;
QMap<IProject*, QVector<QUrl>> Helper::cachedSearchPaths;
QVector<QUrl> Helper::projectSearchPaths;
68
QStringList Helper::dataDirs;
69
IndexedString Helper::documentationFile;
70
DUChainPointer<TopDUContext> Helper::documentationFileContext = DUChainPointer<TopDUContext>(nullptr);
71 72
QStringList Helper::correctionFileDirs;
QString Helper::localCorrectionFileDir;
73
QMutex Helper::cacheMutex;
74
QMutex Helper::projectPathLock;
75

76 77 78 79 80
void Helper::scheduleDependency(const IndexedString& dependency, int betterThanPriority)
{
    BackgroundParser* bgparser = KDevelop::ICore::self()->languageController()->backgroundParser();
    bool needsReschedule = true;
    if ( bgparser->isQueued(dependency) ) {
81 82
        const auto priority= bgparser->priorityForDocument(dependency);
        if ( priority > betterThanPriority - 1 ) {
83 84
            bgparser->removeDocument(dependency);
        }
85
        else {
86 87 88 89 90
            needsReschedule = false;
        }
    }
    if ( needsReschedule ) {
        bgparser->addDocument(dependency, TopDUContext::ForceUpdate, betterThanPriority - 1,
91
                              nullptr, ParseJob::FullSequentialProcessing);
92 93 94
    }
}

95 96 97
IndexedDeclaration Helper::declarationUnderCursor(bool allowUse)
{
    KDevelop::IDocument* doc = ICore::self()->documentController()->activeDocument();
Sven Brauch's avatar
Sven Brauch committed
98 99
    const auto view = static_cast<KDevelop::PartController*>(ICore::self()->partController())->activeView();
    if ( doc && doc->textDocument() && view ) {
100
        DUChainReadLocker lock;
Sven Brauch's avatar
Sven Brauch committed
101
        const auto cursor = view->cursorPosition();
102
        if ( allowUse ) {
103
            return IndexedDeclaration(DUChainUtils::itemUnderCursor(doc->url(), cursor).declaration);
104 105
        }
        else {
Sven Brauch's avatar
Sven Brauch committed
106
            return DUChainUtils::declarationInLine(cursor, DUChainUtils::standardContextForUrl(doc->url()));
107 108 109 110 111 112
        }
    }

    return KDevelop::IndexedDeclaration();
}

113 114 115
Declaration* Helper::accessAttribute(const AbstractType::Ptr accessed,
                                     const IndexedIdentifier& attribute,
                                     const TopDUContext* topContext)
116
{
117
    if ( ! accessed ) {
118
        return nullptr;
119
    }
120
    // if the type is unsure, search all the possibilities (but return the first match)
121
    auto structureTypes = Helper::filterType<StructureType>(accessed,
122
        [](AbstractType::Ptr toFilter) {
123 124
            auto type = Helper::resolveAliasType(toFilter);
            return type && type->whichType() == AbstractType::TypeStructure;
125 126 127
        },
        [](AbstractType::Ptr toMap) {
            return StructureType::Ptr::staticCast(Helper::resolveAliasType(toMap));
128 129
        }
    );
130 131 132 133 134 135 136 137 138 139 140 141
    auto docFileContext = Helper::getDocumentationFileContext();

    for ( const auto& type: structureTypes ) {
        auto searchContexts = Helper::internalContextsForClass(type, topContext);
        for ( const auto ctx: searchContexts ) {
            auto found = ctx->findDeclarations(attribute, CursorInRevision::invalid(),
                                               topContext, DUContext::DontSearchInParent);
            if ( !found.isEmpty() && (
                   found.last()->topContext() != docFileContext ||
                   ctx->topContext() == docFileContext) ) {
                // never consider decls from the builtins
                return found.last();
142
            }
143 144
        }
    }
145
    return nullptr;
146 147
}

148 149
AbstractType::Ptr Helper::resolveAliasType(const AbstractType::Ptr eventualAlias)
{
150
    return TypeUtils::resolveAliasType(eventualAlias);
151 152
}

153
AbstractType::Ptr Helper::extractTypeHints(AbstractType::Ptr type)
154
{
155 156 157 158
    return Helper::foldTypes(Helper::filterType<AbstractType>(type, [](AbstractType::Ptr t) -> bool {
        auto hint = t.cast<HintedType>();
        return !hint || hint->isValid();
    }));
159 160
}

161
Helper::FuncInfo Helper::functionForCalled(Declaration* called, bool isAlias)
162
{
163 164
    if ( ! called ) {
        return { nullptr, false };
165
    }
166 167
    else if ( called->isFunctionDeclaration() ) {
        return { static_cast<FunctionDeclaration*>( called ), false };
168
    }
Sven Brauch's avatar
Sven Brauch committed
169
    // If we're calling a type object (isAlias == true), look for a constructor.
170 171
    static const IndexedIdentifier initId(KDevelop::Identifier("__init__"));

Sven Brauch's avatar
Sven Brauch committed
172
    // Otherwise look for a `__call__()` method.
173 174 175 176
    static const IndexedIdentifier callId(KDevelop::Identifier("__call__"));

    auto attr = accessAttribute(called->abstractType(), (isAlias ? initId : callId), called->topContext());
    return { dynamic_cast<FunctionDeclaration*>(attr), isAlias };
177 178
}

179
Declaration* Helper::declarationForName(const QString& name, const CursorInRevision& location,
180
                                        KDevelop::DUChainPointer<const DUContext> context)
181
{
182
    DUChainReadLocker lock(DUChain::lock());
183
    auto identifier = KDevelop::Identifier(name);
184 185
    auto localDeclarations = context->findLocalDeclarations(identifier, location, nullptr,
                                                            AbstractType::Ptr(), DUContext::DontResolveAliases);
186 187
    if ( !localDeclarations.isEmpty() ) {
        return localDeclarations.last();
188
    }
189 190 191 192 193 194

    QList<Declaration*> declarations;
    const DUContext* currentContext = context.data();
    bool findInNext = true, findBeyondUse = false;
    do {
        if (findInNext) {
195 196
            CursorInRevision findUntil = findBeyondUse ? currentContext->topContext()->range().end : location;
            declarations = currentContext->findDeclarations(identifier, findUntil);
197 198 199 200 201 202 203 204 205

            for (Declaration* declaration: declarations) {
                if (declaration->context()->type() != DUContext::Class ||
                    (currentContext->type() == DUContext::Function && declaration->context() == currentContext->parentContext())) {
                     // Declarations from class decls must be referenced through `self.<foo>`, except
                     //  in their local scope (handled above) or when used as default arguments for methods of the same class.
                     // Otherwise, we're done!
                    return declaration;
                }
206
            }
207 208 209 210
            if (!declarations.isEmpty()) {
                // If we found declarations but rejected all of them (i.e. didn't return), we need to keep searching.
                findInNext = true;
                declarations.clear();
211
            }
212
        }
213

214 215 216 217 218 219 220 221
        if (!findBeyondUse && currentContext->owner() && currentContext->owner()->isFunctionDeclaration()) {
            // Names in the body may be defined after the function definition, before the function is called.
            // Note: only the parameter list has type DUContext::Function, so we have to do this instead.
            findBeyondUse = findInNext = true;
        }
    } while ((currentContext = currentContext->parentContext()));

    return nullptr;
222 223
}

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

Declaration* Helper::declarationForName(const Python::NameAst* name, CursorInRevision location,
                                        KDevelop::DUChainPointer<const DUContext> context)
{
    const Ast* checkNode = name;
    while ((checkNode = checkNode->parent)) {
        switch (checkNode->astType) {
          default:
            continue;
          case Ast::ListComprehensionAstType:
          case Ast::SetComprehensionAstType:
          case Ast::DictionaryComprehensionAstType:
          case Ast::GeneratorExpressionAstType:
            // Variables in comprehensions are used before their definition. `[foo for foo in bar]`
            auto cmpEnd = CursorInRevision(checkNode->endLine, checkNode->endCol);
            if (cmpEnd > location) {
                location = cmpEnd;
            }
        }
    }
    return declarationForName(name->identifier->value, location, context);
}

247 248
QVector<DUContext*> Helper::internalContextsForClass(const StructureType::Ptr classType,
                        const TopDUContext* context, ContextSearchFlags flags, int depth)
249
{
250 251
    QVector<DUContext*> searchContexts;
    if ( ! classType ) {
252 253
        return searchContexts;
    }
254
    if ( auto c = classType->internalContext(context) ) {
255
        searchContexts << c;
256
    }
257 258 259
    auto decl = Helper::resolveAliasDeclaration(classType->declaration(context));
    if ( auto classDecl = dynamic_cast<ClassDeclaration*>(decl) ) {
        FOREACH_FUNCTION ( const auto& base, classDecl->baseClasses ) {
260
            if ( flags == PublicOnly && base.access == KDevelop::Declaration::Private ) {
261 262
                continue;
            }
263
            auto baseClassType = base.baseClass.type<StructureType>();
264
            // recursive call, because the base class will have more base classes eventually
265
            if ( depth < 10 ) {
266
                searchContexts.append(Helper::internalContextsForClass(baseClassType, context, flags, depth + 1));
267
            }
268 269 270 271
        }
    }
    return searchContexts;
}
272 273 274 275 276

Declaration* Helper::resolveAliasDeclaration(Declaration* decl)
{
    AliasDeclaration* alias = dynamic_cast<AliasDeclaration*>(decl);
    if ( alias ) {
Sven Brauch's avatar
Sven Brauch committed
277
        DUChainReadLocker lock;
278 279 280 281 282
        return alias->aliasedDeclaration().data();
    }
    else
        return decl;
}
283

284 285
QStringList Helper::getDataDirs() {
    if ( Helper::dataDirs.isEmpty() ) {
Sven Brauch's avatar
Sven Brauch committed
286
        Helper::dataDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "kdevpythonsupport/documentation_files", QStandardPaths::LocateDirectory);
287
    }
288
    return Helper::dataDirs;
289 290
}

291 292 293 294 295
KDevelop::IndexedString Helper::getDocumentationFile()
{
    if ( Helper::documentationFile.isEmpty() ) {
        auto path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kdevpythonsupport/documentation_files/builtindocumentation.py");
        Helper::documentationFile = IndexedString(path);
296 297 298
    }
    return Helper::documentationFile;
}
299 300 301

ReferencedTopDUContext Helper::getDocumentationFileContext()
{
302 303 304 305
    if ( Helper::documentationFileContext ) {
        return ReferencedTopDUContext(Helper::documentationFileContext.data());
    }
    else {
306
        DUChainReadLocker lock;
307
        auto file = Helper::getDocumentationFile();
308
        ReferencedTopDUContext ctx = ReferencedTopDUContext(DUChain::self()->chainForDocument(file));
309 310 311
        Helper::documentationFileContext = DUChainPointer<TopDUContext>(ctx.data());
        return ctx;
    }
312
}
313

Sven Brauch's avatar
Sven Brauch committed
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
// stolen from KUrl. duh.
static QString _relativePath(const QString &base_dir, const QString &path)
{
   QString _base_dir(QDir::cleanPath(base_dir));
   QString _path(QDir::cleanPath(path.isEmpty() || QDir::isRelativePath(path) ? _base_dir+QLatin1Char('/')+path : path));

   if (_base_dir.isEmpty())
      return _path;

   if (_base_dir[_base_dir.length()-1] != QLatin1Char('/'))
      _base_dir.append(QLatin1Char('/') );

   const QStringList list1 = _base_dir.split(QLatin1Char('/'), QString::SkipEmptyParts);
   const QStringList list2 = _path.split(QLatin1Char('/'), QString::SkipEmptyParts);

   // Find where they meet
   int level = 0;
   int maxLevel = qMin(list1.count(), list2.count());
   while((level < maxLevel) && (list1[level] == list2[level])) level++;

   QString result;
   // Need to go down out of the first path to the common branch.
   for(int i = level; i < list1.count(); i++)
      result.append(QLatin1String("../"));

   // Now up up from the common branch to the second path.
   for(int i = level; i < list2.count(); i++)
      result.append(list2[i]).append(QLatin1Char('/'));

   if ((level < list2.count()) && (path[path.length()-1] != QLatin1Char('/')))
      result.truncate(result.length()-1);

   return result;
}

349
QUrl Helper::getCorrectionFile(const QUrl& document)
350 351
{
    if ( Helper::correctionFileDirs.isEmpty() ) {
Sven Brauch's avatar
Sven Brauch committed
352 353 354
        Helper::correctionFileDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
                                                               "kdevpythonsupport/correction_files/",
                                                               QStandardPaths::LocateDirectory);
355 356 357
    }

    foreach (QString correctionFileDir, correctionFileDirs) {
358
        foreach ( const QUrl& basePath, Helper::getSearchPaths(QUrl()) ) {
359 360 361
            if ( ! basePath.isParentOf(document) ) {
                continue;
            }
Sven Brauch's avatar
Sven Brauch committed
362 363 364 365 366
            auto base = basePath.path();
            auto doc = document.path();
            auto relative = _relativePath(base, doc);
            auto fullPath = correctionFileDir + "/" + relative;
            if ( QFile::exists(fullPath) ) {
367
                return QUrl::fromLocalFile(fullPath).adjusted(QUrl::NormalizePathSegments);
368 369 370
            }
        }
    }
371
    return {};
372 373
}

374
QUrl Helper::getLocalCorrectionFile(const QUrl& document)
375 376
{
    if ( Helper::localCorrectionFileDir.isNull() ) {
377
        Helper::localCorrectionFileDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "kdevpythonsupport/correction_files/";
378 379
    }

380
    auto absolutePath = QUrl();
381
    foreach ( const auto& basePath, Helper::getSearchPaths(QUrl()) ) {
382 383 384
        if ( ! basePath.isParentOf(document) ) {
            continue;
        }
385
        auto path = QDir(basePath.path()).relativeFilePath(document.path());
386
        absolutePath = QUrl::fromLocalFile(Helper::localCorrectionFileDir + path);
387 388 389 390
        break;
    }
    return absolutePath;
}
391

392
QString Helper::getPythonExecutablePath(IProject* project)
393 394 395 396 397 398 399 400 401 402 403 404 405
{
    if ( project ) {
        auto interpreter = project->projectConfiguration()->group("pythonsupport").readEntry("interpreter");
        if ( !interpreter.isEmpty() ) {
            // we have a user-configured interpreter, try using it
            QFile f(interpreter);
            if ( f.exists() ) {
                return interpreter;
            }
            qCWarning(KDEV_PYTHON_DUCHAIN) << "Custom python interpreter" << interpreter << "configured for project" << project->name() << "is invalid, using default";
        }
    }

406
    // Find python 3 (https://www.python.org/dev/peps/pep-0394/)
407
    auto result = QStandardPaths::findExecutable("python" PYTHON_VERSION_STR);
408 409 410
    if ( ! result.isEmpty() ) {
        return result;
    }
411
    result = QStandardPaths::findExecutable("python" PYTHON_VERSION_MAJOR_STR);
412 413 414 415
    if ( ! result.isEmpty() ) {
        return result;
    }
    result = QStandardPaths::findExecutable("python");
416 417 418 419 420 421 422 423 424 425 426 427 428 429
    if ( ! result.isEmpty() ) {
        return result;
    }

#ifdef Q_OS_WIN
    QStringList extraPaths;
    // Check for default CPython installation path, because the
    // installer does not add the path to $PATH.
    QStringList keys = {
        "HKEY_LOCAL_MACHINE\\Software\\Python\\PythonCore\\PYTHON_VERSION\\InstallPath",
        "HKEY_LOCAL_MACHINE\\Software\\Python\\PythonCore\\PYTHON_VERSION-32\\InstallPath",
        "HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\PYTHON_VERSION\\InstallPath",
        "HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\PYTHON_VERSION-32\\InstallPath"
    };
430
    auto version = QString(PYTHON_VERSION_STR);
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
    foreach ( QString key, keys ) {
        key.replace("PYTHON_VERSION", version);
        QSettings base(key.left(key.indexOf("Python")), QSettings::NativeFormat);
        if ( ! base.childGroups().contains("Python") ) {
            continue;
        }
        QSettings keySettings(key, QSettings::NativeFormat);
        auto path = keySettings.value("Default").toString();
        if ( ! path.isEmpty() ) {
            extraPaths << path;
            break;
        }
    }
    result = QStandardPaths::findExecutable("python", extraPaths);
    if ( ! result.isEmpty() ) {
        return result;
    }
#endif
    // fallback
    return PYTHON_EXECUTABLE;
}

453
QVector<QUrl> Helper::getSearchPaths(const QUrl& workingOnDocument)
Sven Brauch's avatar
Sven Brauch committed
454
{
455
    QMutexLocker lock(&Helper::cacheMutex);
456
    QVector<QUrl> searchPaths;
Sven Brauch's avatar
Sven Brauch committed
457
    // search in the projects, as they're packages and likely to be installed or added to PYTHONPATH later
458
    // and also add custom include paths that are defined in the projects
459

460
    auto project = ICore::self()->projectController()->findProjectForUrl(workingOnDocument);
461 462 463
    {
        QMutexLocker lock(&Helper::projectPathLock);
        searchPaths << Helper::projectSearchPaths;
464
        searchPaths << Helper::cachedCustomIncludes.value(project);
465
    }
466
    
467
    foreach ( const QString& path, getDataDirs() ) {
468
        searchPaths.append(QUrl::fromLocalFile(path));
469
    }
470

471 472 473
    if ( !cachedSearchPaths.contains(project) ) {
        QVector<QUrl> cachedForProject;
        qCDebug(KDEV_PYTHON_DUCHAIN) << "*** Collecting search paths...";
474
        QStringList getpath;
475
        getpath << "-c" << "import sys; sys.stdout.write('$|$'.join(sys.path))";
476 477
        
        QProcess python;
478
        python.start(getPythonExecutablePath(project), getpath);
479
        python.waitForFinished(1000);
480
        QString pythonpath = QString::fromUtf8(python.readAllStandardOutput());
481

482
        if ( ! pythonpath.isEmpty() ) {
483
            const auto paths = pythonpath.split("$|$", QString::SkipEmptyParts);
484
            foreach ( const QString& path, paths ) {
485
                cachedForProject.append(QUrl::fromLocalFile(path));
486 487 488
            }
        }
        else {
489
            qCWarning(KDEV_PYTHON_DUCHAIN) << "Could not get search paths! Defaulting to stupid stuff.";
490 491
            searchPaths.append(QUrl::fromLocalFile("/usr/lib/python" PYTHON_VERSION_STR));
            searchPaths.append(QUrl::fromLocalFile("/usr/lib/python" PYTHON_VERSION_STR "/site-packages"));
492
            QString path = qgetenv("PYTHONPATH");
493 494
            QStringList paths = path.split(':');
            foreach ( const QString& path, paths ) {
495
                cachedForProject.append(QUrl::fromLocalFile(path));
496 497
            }
        }
498
        qCDebug(KDEV_PYTHON_DUCHAIN) << " *** Done. Got search paths: " << cachedSearchPaths;
499
        cachedSearchPaths.insert(project, cachedForProject);
500
    }
501
    
502
    searchPaths.append(cachedSearchPaths.value(project));
Sven Brauch's avatar
Sven Brauch committed
503
    
504 505
    auto dir = workingOnDocument.adjusted(QUrl::RemoveFilename);
    if ( ! dir.isEmpty() ) {
506
        // search in the current packages
507
        searchPaths.append(dir);
508
    }
Sven Brauch's avatar
Sven Brauch committed
509 510 511 512
    
    return searchPaths;
}

513 514
bool Helper::isUsefulType(AbstractType::Ptr type)
{
515
    return TypeUtils::isUsefulType(type);
516 517
}

518
AbstractType::Ptr Helper::contentOfIterable(const AbstractType::Ptr iterable, const TopDUContext* topContext)
Sven Brauch's avatar
Sven Brauch committed
519
{
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
    auto types = filterType<StructureType>(iterable,
        [](AbstractType::Ptr t) { return t->whichType() == AbstractType::TypeStructure; } );

    static const IndexedIdentifier iterId(KDevelop::Identifier("__iter__"));
    static const IndexedIdentifier nextId(KDevelop::Identifier("__next__"));
    AbstractType::Ptr content(new IntegralType(IntegralType::TypeMixed));

    for ( const auto& type: types ) {
        if ( auto map = type.cast<MapType>() ) {
            // Iterating over dicts gets keys, not values
            content = mergeTypes(content, map->keyType().abstractType());
            continue;
        }
        else if ( auto list = type.cast<ListType>() ) {
            content = mergeTypes(content, list->contentType().abstractType());
            continue;
        }
        else if ( auto indexed = type.cast<IndexedContainer>() ) {
            content = mergeTypes(content, indexed->asUnsureType());
            continue;
        }
        DUChainReadLocker lock;
        // Content of an iterable object is iterable.__iter__().__next__().
        if ( auto iterFunc = dynamic_cast<FunctionDeclaration*>(accessAttribute(type, iterId, topContext)) ) {
            if ( auto iterator = iterFunc->type<FunctionType>()->returnType().cast<StructureType>() ) {
                if ( auto nextFunc = dynamic_cast<FunctionDeclaration*>(accessAttribute(iterator, nextId, topContext)) ) {
                    content = mergeTypes(content, nextFunc->type<FunctionType>()->returnType());
                }
Sven Brauch's avatar
Sven Brauch committed
548 549 550
            }
        }
    }
551
    return content;
Sven Brauch's avatar
Sven Brauch committed
552 553
}

554
AbstractType::Ptr Helper::mergeTypes(AbstractType::Ptr type, const AbstractType::Ptr newType)
555
{
556
    UnsureType::Ptr ret;
557
    return TypeUtils::mergeTypes<Python::UnsureType>(type, newType);
558 559
}

560
}