From 27d08306986c72aeab1b1f06e96e7072c53d3622 Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Tue, 23 Aug 2011 12:02:29 +0200
Subject: [PATCH] QmlJS: Move the exported-C++-type detection out of C++ code.

It now lives in qmljstools/qmljsfindexportedcpptypes, all in one place.

Also ensures that the source code is available when a file is being
scanned for QML exports. This will enable checking comments for
annotations about the URI a plugin is usually imported as.

Change-Id: I1da36d0678e0a8d34b171dbe0f6b5690d89eb18b
Reviewed-on: http://codereview.qt.nokia.com/3392
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
---
 src/libs/cplusplus/CppDocument.cpp            | 259 +---------
 src/libs/cplusplus/CppDocument.h              |  23 +-
 src/libs/cplusplus/ModelManagerInterface.h    |   2 -
 src/libs/qmljs/qmljslink.cpp                  |   4 +-
 src/plugins/cpptools/cppmodelmanager.cpp      | 169 +-----
 src/plugins/cpptools/cppmodelmanager.h        |   2 -
 .../qmljstools/qmljsfindexportedcpptypes.cpp  | 486 ++++++++++++++++++
 .../qmljstools/qmljsfindexportedcpptypes.h    |  55 ++
 src/plugins/qmljstools/qmljsmodelmanager.cpp  |  51 +-
 src/plugins/qmljstools/qmljsmodelmanager.h    |   9 +-
 src/plugins/qmljstools/qmljstools-lib.pri     |   6 +-
 11 files changed, 616 insertions(+), 450 deletions(-)
 create mode 100644 src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
 create mode 100644 src/plugins/qmljstools/qmljsfindexportedcpptypes.h

diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index a72648b8ba2..169ec2b694c 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -256,7 +256,8 @@ Document::Document(const QString &fileName)
     : _fileName(QDir::cleanPath(fileName)),
       _globalNamespace(0),
       _revision(0),
-      _editorRevision(0)
+      _editorRevision(0),
+      _fastCheck(false)
 {
     _control = new Control();
 
@@ -574,8 +575,10 @@ void Document::check(CheckMode mode)
 
     _globalNamespace = _control->newNamespace(0);
     Bind semantic(_translationUnit);
-    if (mode == FastCheck)
+    if (mode == FastCheck) {
+        _fastCheck = true;
         semantic.setSkipFunctionBodies(true);
+    }
 
     if (! _translationUnit->ast())
         return; // nothing to do.
@@ -589,253 +592,19 @@ void Document::check(CheckMode mode)
     }
 }
 
-class FindExposedQmlTypes : protected ASTVisitor
+void Document::keepSourceAndAST()
 {
-    Document *_doc;
-    QList<Document::ExportedQmlType> _exportedTypes;
-    CompoundStatementAST *_compound;
-    ASTMatcher _matcher;
-    ASTPatternBuilder _builder;
-    Overview _overview;
-
-public:
-    FindExposedQmlTypes(Document *doc)
-        : ASTVisitor(doc->translationUnit())
-        , _doc(doc)
-        , _compound(0)
-    {}
-
-    QList<Document::ExportedQmlType> operator()()
-    {
-        _exportedTypes.clear();
-        accept(translationUnit()->ast());
-        return _exportedTypes;
-    }
-
-protected:
-    virtual bool visit(CompoundStatementAST *ast)
-    {
-        CompoundStatementAST *old = _compound;
-        _compound = ast;
-        accept(ast->statement_list);
-        _compound = old;
-        return false;
-    }
-
-    virtual bool visit(CallAST *ast)
-    {
-        IdExpressionAST *idExp = ast->base_expression->asIdExpression();
-        if (!idExp || !idExp->name)
-            return false;
-        TemplateIdAST *templateId = idExp->name->asTemplateId();
-        if (!templateId || !templateId->identifier_token)
-            return false;
-
-        // check the name
-        const Identifier *templateIdentifier = translationUnit()->identifier(templateId->identifier_token);
-        if (!templateIdentifier)
-            return false;
-        const QString callName = QString::fromUtf8(templateIdentifier->chars());
-        if (callName != QLatin1String("qmlRegisterType"))
-            return false;
-
-        // must have a single typeid template argument
-        if (!templateId->template_argument_list || !templateId->template_argument_list->value
-                || templateId->template_argument_list->next)
-            return false;
-        TypeIdAST *typeId = templateId->template_argument_list->value->asTypeId();
-        if (!typeId)
-            return false;
-
-        // must have four arguments
-        if (!ast->expression_list
-                || !ast->expression_list->value || !ast->expression_list->next
-                || !ast->expression_list->next->value || !ast->expression_list->next->next
-                || !ast->expression_list->next->next->value || !ast->expression_list->next->next->next
-                || !ast->expression_list->next->next->next->value
-                || ast->expression_list->next->next->next->next)
-            return false;
-
-        // last argument must be a string literal
-        const StringLiteral *nameLit = 0;
-        if (StringLiteralAST *nameAst = ast->expression_list->next->next->next->value->asStringLiteral())
-            nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
-        if (!nameLit) {
-            // disable this warning for now, we don't want to encourage using string literals if they don't mean to
-            // in the future, we will also accept annotations for the qmlRegisterType arguments in comments
-//            translationUnit()->warning(ast->expression_list->next->next->next->value->firstToken(),
-//                                       "The type will only be available in Qt Creator's QML editors when the type name is a string literal");
-            return false;
-        }
-
-        // if the first argument is a string literal, things are easy
-        QString packageName;
-        if (StringLiteralAST *packageAst = ast->expression_list->value->asStringLiteral()) {
-            const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
-            packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
-        }
-        // as a special case, allow an identifier package argument if there's a
-        // Q_ASSERT(QLatin1String(uri) == QLatin1String("actual uri"));
-        // in the enclosing compound statement
-        IdExpressionAST *uriName = ast->expression_list->value->asIdExpression();
-        if (packageName.isEmpty() && uriName && _compound) {
-            for (StatementListAST *it = _compound->statement_list; it; it = it->next) {
-                StatementAST *stmt = it->value;
-
-                packageName = nameOfUriAssert(stmt, uriName);
-                if (!packageName.isEmpty())
-                    break;
-            }
-        }
-
-        // second and third argument must be integer literals
-        const NumericLiteral *majorLit = 0;
-        const NumericLiteral *minorLit = 0;
-        if (NumericLiteralAST *majorAst = ast->expression_list->next->value->asNumericLiteral())
-            majorLit = translationUnit()->numericLiteral(majorAst->literal_token);
-        if (NumericLiteralAST *minorAst = ast->expression_list->next->next->value->asNumericLiteral())
-            minorLit = translationUnit()->numericLiteral(minorAst->literal_token);
-
-        // build the descriptor
-        Document::ExportedQmlType exportedType;
-        exportedType.typeName = QString::fromUtf8(nameLit->chars(), nameLit->size());
-        if (!packageName.isEmpty() && majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
-            exportedType.packageName = packageName;
-            exportedType.majorVersion = QString::fromUtf8(majorLit->chars(), majorLit->size()).toInt();
-            exportedType.minorVersion = QString::fromUtf8(minorLit->chars(), minorLit->size()).toInt();
-        } else {
-            // disable this warning, see above for details
-//            translationUnit()->warning(ast->base_expression->firstToken(),
-//                                       "The module will not be available in Qt Creator's QML editors because the uri and version numbers\n"
-//                                       "cannot be determined by static analysis. The type will still be available globally.");
-            exportedType.packageName = QLatin1String("<default>");
-        }
-
-        // we want to do lookup later, so also store the surrounding scope
-        unsigned line, column;
-        translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
-        exportedType.scope = _doc->scopeAt(line, column);
-
-        // and the expression
-        const Token begin = translationUnit()->tokenAt(typeId->firstToken());
-        const Token last = translationUnit()->tokenAt(typeId->lastToken() - 1);
-        exportedType.typeExpression = _doc->source().mid(begin.begin(), last.end() - begin.begin());
-
-        _exportedTypes += exportedType;
-
-        return false;
-    }
-
-private:
-    QString stringOf(AST *ast)
-    {
-        const Token begin = translationUnit()->tokenAt(ast->firstToken());
-        const Token last = translationUnit()->tokenAt(ast->lastToken() - 1);
-        return _doc->source().mid(begin.begin(), last.end() - begin.begin());
-    }
-
-    ExpressionAST *skipStringCall(ExpressionAST *exp)
-    {
-        if (!exp)
-            return 0;
-
-        IdExpressionAST *callName = _builder.IdExpression();
-        CallAST *call = _builder.Call(callName);
-        if (!exp->match(call, &_matcher))
-            return exp;
-
-        const QString name = stringOf(callName);
-        if (name != QLatin1String("QLatin1String")
-                && name != QLatin1String("QString"))
-            return exp;
-
-        if (!call->expression_list || call->expression_list->next)
-            return exp;
-
-        return call->expression_list->value;
-    }
-
-    QString nameOfUriAssert(StatementAST *stmt, IdExpressionAST *uriName)
-    {
-        QString null;
-
-        IdExpressionAST *outerCallName = _builder.IdExpression();
-        BinaryExpressionAST *binary = _builder.BinaryExpression();
-        // assert(... == ...);
-        ExpressionStatementAST *pattern = _builder.ExpressionStatement(
-                    _builder.Call(outerCallName, _builder.ExpressionList(
-                                     binary)));
-
-        if (!stmt->match(pattern, &_matcher)) {
-            outerCallName = _builder.IdExpression();
-            binary = _builder.BinaryExpression();
-            // the expansion of Q_ASSERT(...),
-            // ((!(... == ...)) ? qt_assert(...) : ...);
-            pattern = _builder.ExpressionStatement(
-                        _builder.NestedExpression(
-                            _builder.ConditionalExpression(
-                                _builder.NestedExpression(
-                                    _builder.UnaryExpression(
-                                        _builder.NestedExpression(
-                                            binary))),
-                                _builder.Call(outerCallName))));
-
-            if (!stmt->match(pattern, &_matcher))
-                return null;
-        }
-
-        const QString outerCall = stringOf(outerCallName);
-        if (outerCall != QLatin1String("qt_assert")
-                && outerCall != QLatin1String("assert")
-                && outerCall != QLatin1String("Q_ASSERT"))
-            return null;
-
-        if (translationUnit()->tokenAt(binary->binary_op_token).kind() != T_EQUAL_EQUAL)
-            return null;
-
-        ExpressionAST *lhsExp = skipStringCall(binary->left_expression);
-        ExpressionAST *rhsExp = skipStringCall(binary->right_expression);
-        if (!lhsExp || !rhsExp)
-            return null;
-
-        StringLiteralAST *uriString = lhsExp->asStringLiteral();
-        IdExpressionAST *uriArgName = lhsExp->asIdExpression();
-        if (!uriString)
-            uriString = rhsExp->asStringLiteral();
-        if (!uriArgName)
-            uriArgName = rhsExp->asIdExpression();
-        if (!uriString || !uriArgName)
-            return null;
-
-        if (stringOf(uriArgName) != stringOf(uriName))
-            return null;
-
-        const StringLiteral *packageLit = translationUnit()->stringLiteral(uriString->literal_token);
-        return QString::fromUtf8(packageLit->chars(), packageLit->size());
-    }
-};
-
-void Document::findExposedQmlTypes()
-{
-    if (! _translationUnit->ast())
-        return;
-
-    QByteArray qmlRegisterTypeToken("qmlRegisterType");
-    if (_translationUnit->control()->findIdentifier(
-                qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
-        FindExposedQmlTypes finder(this);
-        _exportedQmlTypes = finder();
-    }
-}
-
-void Document::releaseSource()
-{
-    _source.clear();
+    _keepSourceAndASTCount.ref();
 }
 
-void Document::releaseTranslationUnit()
+void Document::releaseSourceAndAST()
 {
-    _translationUnit->release();
+    if (!_keepSourceAndASTCount.deref()) {
+        _source.clear();
+        _translationUnit->release();
+        if (_fastCheck)
+            _control->squeeze();
+    }
 }
 
 Snapshot::Snapshot()
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index 48d72cad3cb..024fec93fb5 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -40,6 +40,7 @@
 #include <QtCore/QDateTime>
 #include <QtCore/QHash>
 #include <QtCore/QFileInfo>
+#include <QtCore/QAtomicInt>
 
 namespace CPlusPlus {
 
@@ -125,11 +126,6 @@ public:
 
     void check(CheckMode mode = FullCheck);
 
-    void findExposedQmlTypes();
-
-    void releaseSource();
-    void releaseTranslationUnit();
-
     static Ptr create(const QString &fileName);
 
     class DiagnosticMessage
@@ -320,18 +316,8 @@ public:
     const MacroUse *findMacroUseAt(unsigned offset) const;
     const UndefinedMacroUse *findUndefinedMacroUseAt(unsigned offset) const;
 
-    class ExportedQmlType {
-    public:
-        QString packageName;
-        QString typeName;
-        int majorVersion;
-        int minorVersion;
-        Scope *scope;
-        QString typeExpression;
-    };
-
-    QList<ExportedQmlType> exportedQmlTypes() const
-    { return _exportedQmlTypes; }
+    void keepSourceAndAST();
+    void releaseSourceAndAST();
 
 private:
     QString _fileName;
@@ -344,11 +330,12 @@ private:
     QList<Block> _skippedBlocks;
     QList<MacroUse> _macroUses;
     QList<UndefinedMacroUse> _undefinedMacroUses;
-    QList<ExportedQmlType> _exportedQmlTypes;
     QByteArray _source;
     QDateTime _lastModified;
+    QAtomicInt _keepSourceAndASTCount;
     unsigned _revision;
     unsigned _editorRevision;
+    bool _fastCheck;
 
     friend class Snapshot;
 };
diff --git a/src/libs/cplusplus/ModelManagerInterface.h b/src/libs/cplusplus/ModelManagerInterface.h
index 0a9cf6eaeef..75d084053a7 100644
--- a/src/libs/cplusplus/ModelManagerInterface.h
+++ b/src/libs/cplusplus/ModelManagerInterface.h
@@ -140,8 +140,6 @@ public:
 
     virtual void findMacroUsages(const CPlusPlus::Macro &macro) = 0;
 
-    virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const = 0;
-
 Q_SIGNALS:
     void documentUpdated(CPlusPlus::Document::Ptr doc);
     void sourceFilesRefreshed(const QStringList &files);
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 7c2eaaa8166..d43dbbe05c6 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -502,7 +502,9 @@ void Link::loadImplicitDefaultImports(Imports *imports)
             import.info = info;
             import.object = new ObjectValue(d->valueOwner);
             foreach (QmlObjectValue *object,
-                     d->valueOwner->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) {
+                     d->valueOwner->cppQmlTypes().typesForImport(
+                         defaultPackage,
+                         ComponentVersion(ComponentVersion::MaxVersion, ComponentVersion::MaxVersion))) {
                 import.object->setMember(object->className(), object);
             }
             d->importCache.insert(ImportCacheKey(info), import);
diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp
index 489a58c03cb..1aab932648a 100644
--- a/src/plugins/cpptools/cppmodelmanager.cpp
+++ b/src/plugins/cpptools/cppmodelmanager.cpp
@@ -290,15 +290,11 @@ public:
     void operator()()
     {
         _doc->check(_mode);
-        _doc->findExposedQmlTypes();
-        _doc->releaseSource();
-        _doc->releaseTranslationUnit();
-
-        if (_mode == Document::FastCheck)
-            _doc->control()->squeeze();
 
         if (_modelManager)
             _modelManager->emitDocumentUpdated(_doc); // ### TODO: compress
+
+        _doc->releaseSourceAndAST();
     }
 };
 } // end of anonymous namespace
@@ -590,6 +586,7 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
     const QByteArray preprocessedCode = preprocess(fileName, contents);
 
     doc->setSource(preprocessedCode);
+    doc->keepSourceAndAST();
     doc->tokenize();
 
     snapshot.insert(doc);
@@ -1284,166 +1281,6 @@ void CppModelManager::GC()
     protectSnapshot.unlock();
 }
 
-static FullySpecifiedType stripPointerAndReference(const FullySpecifiedType &type)
-{
-    Type *t = type.type();
-    while (t) {
-        if (PointerType *ptr = t->asPointerType())
-            t = ptr->elementType().type();
-        else if (ReferenceType *ref = t->asReferenceType())
-            t = ref->elementType().type();
-        else
-            break;
-    }
-    return FullySpecifiedType(t);
-}
-
-static QString toQmlType(const FullySpecifiedType &type)
-{
-    Overview overview;
-    QString result = overview(stripPointerAndReference(type));
-    if (result == QLatin1String("QString"))
-        result = QLatin1String("string");
-    return result;
-}
-
-static Class *lookupClass(const QString &expression, Scope *scope, TypeOfExpression &typeOf)
-{
-    QList<LookupItem> results = typeOf(expression, scope);
-    Class *klass = 0;
-    foreach (const LookupItem &item, results) {
-        if (item.declaration()) {
-            klass = item.declaration()->asClass();
-            if (klass)
-                return klass;
-        }
-    }
-    return 0;
-}
-
-static void populate(LanguageUtils::FakeMetaObject::Ptr fmo, Class *klass,
-                     QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *classes,
-                     TypeOfExpression &typeOf)
-{
-    using namespace LanguageUtils;
-
-    Overview namePrinter;
-
-    classes->insert(klass, fmo);
-
-    for (unsigned i = 0; i < klass->memberCount(); ++i) {
-        Symbol *member = klass->memberAt(i);
-        if (!member->name())
-            continue;
-        if (Function *func = member->type()->asFunctionType()) {
-            if (!func->isSlot() && !func->isInvokable() && !func->isSignal())
-                continue;
-            FakeMetaMethod method(namePrinter(func->name()), toQmlType(func->returnType()));
-            if (func->isSignal())
-                method.setMethodType(FakeMetaMethod::Signal);
-            else
-                method.setMethodType(FakeMetaMethod::Slot);
-            for (unsigned a = 0; a < func->argumentCount(); ++a) {
-                Symbol *arg = func->argumentAt(a);
-                QString name(CppModelManager::tr("unnamed"));
-                if (arg->name())
-                    name = namePrinter(arg->name());
-                method.addParameter(name, toQmlType(arg->type()));
-            }
-            fmo->addMethod(method);
-        }
-        if (QtPropertyDeclaration *propDecl = member->asQtPropertyDeclaration()) {
-            const FullySpecifiedType &type = propDecl->type();
-            const bool isList = false; // ### fixme
-            const bool isWritable = propDecl->flags() & QtPropertyDeclaration::WriteFunction;
-            const bool isPointer = type.type() && type.type()->isPointerType();
-            const int revision = 0; // ### fixme
-            FakeMetaProperty property(
-                        namePrinter(propDecl->name()),
-                        toQmlType(type),
-                        isList, isWritable, isPointer,
-                        revision);
-            fmo->addProperty(property);
-        }
-        if (QtEnum *qtEnum = member->asQtEnum()) {
-            // find the matching enum
-            Enum *e = 0;
-            QList<LookupItem> result = typeOf(namePrinter(qtEnum->name()), klass);
-            foreach (const LookupItem &item, result) {
-                if (item.declaration()) {
-                    e = item.declaration()->asEnum();
-                    if (e)
-                        break;
-                }
-            }
-            if (!e)
-                continue;
-
-            FakeMetaEnum metaEnum(namePrinter(e->name()));
-            for (unsigned j = 0; j < e->memberCount(); ++j) {
-                Symbol *enumMember = e->memberAt(j);
-                if (!enumMember->name())
-                    continue;
-                metaEnum.addKey(namePrinter(enumMember->name()), 0);
-            }
-            fmo->addEnum(metaEnum);
-        }
-    }
-
-    // only single inheritance is supported
-    if (klass->baseClassCount() > 0) {
-        BaseClass *base = klass->baseClassAt(0);
-        if (!base->name())
-            return;
-
-        const QString baseClassName = namePrinter(base->name());
-        fmo->setSuperclassName(baseClassName);
-
-        Class *baseClass = lookupClass(baseClassName, klass, typeOf);
-        if (!baseClass)
-            return;
-
-        FakeMetaObject::Ptr baseFmo = classes->value(baseClass);
-        if (!baseFmo) {
-            baseFmo = FakeMetaObject::Ptr(new FakeMetaObject);
-            populate(baseFmo, baseClass, classes, typeOf);
-        }
-    }
-}
-
-QList<LanguageUtils::FakeMetaObject::ConstPtr> CppModelManager::exportedQmlObjects(const Document::Ptr &doc) const
-{
-    using namespace LanguageUtils;
-    QList<FakeMetaObject::ConstPtr> exportedObjects;
-    QHash<Class *, FakeMetaObject::Ptr> classes;
-
-    const QList<CPlusPlus::Document::ExportedQmlType> exported = doc->exportedQmlTypes();
-    if (exported.isEmpty())
-        return exportedObjects;
-
-    TypeOfExpression typeOf;
-    const Snapshot currentSnapshot = snapshot();
-    typeOf.init(doc, currentSnapshot);
-    foreach (const Document::ExportedQmlType &exportedType, exported) {
-        FakeMetaObject::Ptr fmo(new FakeMetaObject);
-        fmo->addExport(exportedType.typeName, exportedType.packageName,
-                       ComponentVersion(exportedType.majorVersion, exportedType.minorVersion));
-        exportedObjects += fmo;
-
-        Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
-        if (!klass)
-            continue;
-
-        // add the no-package export, so the cpp name can be used in properties
-        Overview overview;
-        fmo->addExport(overview(klass->name()), QString(), ComponentVersion());
-
-        populate(fmo, klass, &classes, typeOf);
-    }
-
-    return exportedObjects;
-}
-
 void CppModelManager::finishedRefreshingSourceFiles(const QStringList &files)
 {
     emit sourceFilesRefreshed(files);
diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h
index d902f7d4d14..13a1b851955 100644
--- a/src/plugins/cpptools/cppmodelmanager.h
+++ b/src/plugins/cpptools/cppmodelmanager.h
@@ -128,8 +128,6 @@ public:
 
     virtual void findMacroUsages(const CPlusPlus::Macro &macro);
 
-    virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const;
-
     void finishedRefreshingSourceFiles(const QStringList &files);
 
 Q_SIGNALS:
diff --git a/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
new file mode 100644
index 00000000000..82e6dcb085d
--- /dev/null
+++ b/src/plugins/qmljstools/qmljsfindexportedcpptypes.cpp
@@ -0,0 +1,486 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#include "qmljsfindexportedcpptypes.h"
+
+#include <qmljs/qmljsinterpreter.h>
+#include <cplusplus/AST.h>
+#include <cplusplus/TranslationUnit.h>
+#include <cplusplus/ASTVisitor.h>
+#include <cplusplus/ASTMatcher.h>
+#include <cplusplus/ASTPatternBuilder.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
+#include <cplusplus/Names.h>
+#include <cplusplus/Literals.h>
+#include <cplusplus/CoreTypes.h>
+#include <cplusplus/Symbols.h>
+#include <utils/qtcassert.h>
+
+#include <QtCore/QDebug>
+
+using namespace QmlJSTools;
+
+namespace {
+using namespace CPlusPlus;
+
+class ExportedQmlType {
+public:
+    QString packageName;
+    QString typeName;
+    LanguageUtils::ComponentVersion version;
+    Scope *scope;
+    QString typeExpression;
+};
+
+class FindExportsVisitor : protected ASTVisitor
+{
+    CPlusPlus::Document::Ptr _doc;
+    QList<ExportedQmlType> _exportedTypes;
+    CompoundStatementAST *_compound;
+    ASTMatcher _matcher;
+    ASTPatternBuilder _builder;
+    Overview _overview;
+
+public:
+    FindExportsVisitor(CPlusPlus::Document::Ptr doc)
+        : ASTVisitor(doc->translationUnit())
+        , _doc(doc)
+        , _compound(0)
+    {}
+
+    QList<ExportedQmlType> operator()()
+    {
+        _exportedTypes.clear();
+        accept(translationUnit()->ast());
+        return _exportedTypes;
+    }
+
+protected:
+    virtual bool visit(CompoundStatementAST *ast)
+    {
+        CompoundStatementAST *old = _compound;
+        _compound = ast;
+        accept(ast->statement_list);
+        _compound = old;
+        return false;
+    }
+
+    virtual bool visit(CallAST *ast)
+    {
+        IdExpressionAST *idExp = ast->base_expression->asIdExpression();
+        if (!idExp || !idExp->name)
+            return false;
+        TemplateIdAST *templateId = idExp->name->asTemplateId();
+        if (!templateId || !templateId->identifier_token)
+            return false;
+
+        // check the name
+        const Identifier *templateIdentifier = translationUnit()->identifier(templateId->identifier_token);
+        if (!templateIdentifier)
+            return false;
+        const QString callName = QString::fromUtf8(templateIdentifier->chars());
+        if (callName != QLatin1String("qmlRegisterType"))
+            return false;
+
+        // must have a single typeid template argument
+        if (!templateId->template_argument_list || !templateId->template_argument_list->value
+                || templateId->template_argument_list->next)
+            return false;
+        TypeIdAST *typeId = templateId->template_argument_list->value->asTypeId();
+        if (!typeId)
+            return false;
+
+        // must have four arguments
+        if (!ast->expression_list
+                || !ast->expression_list->value || !ast->expression_list->next
+                || !ast->expression_list->next->value || !ast->expression_list->next->next
+                || !ast->expression_list->next->next->value || !ast->expression_list->next->next->next
+                || !ast->expression_list->next->next->next->value
+                || ast->expression_list->next->next->next->next)
+            return false;
+
+        // last argument must be a string literal
+        const StringLiteral *nameLit = 0;
+        if (StringLiteralAST *nameAst = ast->expression_list->next->next->next->value->asStringLiteral())
+            nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
+        if (!nameLit) {
+            // disable this warning for now, we don't want to encourage using string literals if they don't mean to
+            // in the future, we will also accept annotations for the qmlRegisterType arguments in comments
+//            translationUnit()->warning(ast->expression_list->next->next->next->value->firstToken(),
+//                                       "The type will only be available in Qt Creator's QML editors when the type name is a string literal");
+            return false;
+        }
+
+        // if the first argument is a string literal, things are easy
+        QString packageName;
+        if (StringLiteralAST *packageAst = ast->expression_list->value->asStringLiteral()) {
+            const StringLiteral *packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
+            packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
+        }
+        // as a special case, allow an identifier package argument if there's a
+        // Q_ASSERT(QLatin1String(uri) == QLatin1String("actual uri"));
+        // in the enclosing compound statement
+        IdExpressionAST *uriName = ast->expression_list->value->asIdExpression();
+        if (packageName.isEmpty() && uriName && _compound) {
+            for (StatementListAST *it = _compound->statement_list; it; it = it->next) {
+                StatementAST *stmt = it->value;
+
+                packageName = nameOfUriAssert(stmt, uriName);
+                if (!packageName.isEmpty())
+                    break;
+            }
+        }
+
+        // second and third argument must be integer literals
+        const NumericLiteral *majorLit = 0;
+        const NumericLiteral *minorLit = 0;
+        if (NumericLiteralAST *majorAst = ast->expression_list->next->value->asNumericLiteral())
+            majorLit = translationUnit()->numericLiteral(majorAst->literal_token);
+        if (NumericLiteralAST *minorAst = ast->expression_list->next->next->value->asNumericLiteral())
+            minorLit = translationUnit()->numericLiteral(minorAst->literal_token);
+
+        // build the descriptor
+        ExportedQmlType exportedType;
+        exportedType.typeName = QString::fromUtf8(nameLit->chars(), nameLit->size());
+        if (!packageName.isEmpty() && majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
+            exportedType.packageName = packageName;
+            exportedType.version = LanguageUtils::ComponentVersion(
+                        QString::fromUtf8(majorLit->chars(), majorLit->size()).toInt(),
+                        QString::fromUtf8(minorLit->chars(), minorLit->size()).toInt());
+        } else {
+            // disable this warning, see above for details
+//            translationUnit()->warning(ast->base_expression->firstToken(),
+//                                       "The module will not be available in Qt Creator's QML editors because the uri and version numbers\n"
+//                                       "cannot be determined by static analysis. The type will still be available globally.");
+            exportedType.packageName = QmlJS::CppQmlTypes::defaultPackage;
+        }
+
+        // we want to do lookup later, so also store the surrounding scope
+        unsigned line, column;
+        translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
+        exportedType.scope = _doc->scopeAt(line, column);
+
+        // and the expression
+        const Token begin = translationUnit()->tokenAt(typeId->firstToken());
+        const Token last = translationUnit()->tokenAt(typeId->lastToken() - 1);
+        exportedType.typeExpression = _doc->source().mid(begin.begin(), last.end() - begin.begin());
+
+        _exportedTypes += exportedType;
+
+        return false;
+    }
+
+private:
+    QString stringOf(AST *ast)
+    {
+        const Token begin = translationUnit()->tokenAt(ast->firstToken());
+        const Token last = translationUnit()->tokenAt(ast->lastToken() - 1);
+        return _doc->source().mid(begin.begin(), last.end() - begin.begin());
+    }
+
+    ExpressionAST *skipStringCall(ExpressionAST *exp)
+    {
+        if (!exp)
+            return 0;
+
+        IdExpressionAST *callName = _builder.IdExpression();
+        CallAST *call = _builder.Call(callName);
+        if (!exp->match(call, &_matcher))
+            return exp;
+
+        const QString name = stringOf(callName);
+        if (name != QLatin1String("QLatin1String")
+                && name != QLatin1String("QString"))
+            return exp;
+
+        if (!call->expression_list || call->expression_list->next)
+            return exp;
+
+        return call->expression_list->value;
+    }
+
+    QString nameOfUriAssert(StatementAST *stmt, IdExpressionAST *uriName)
+    {
+        QString null;
+
+        IdExpressionAST *outerCallName = _builder.IdExpression();
+        BinaryExpressionAST *binary = _builder.BinaryExpression();
+        // assert(... == ...);
+        ExpressionStatementAST *pattern = _builder.ExpressionStatement(
+                    _builder.Call(outerCallName, _builder.ExpressionList(
+                                     binary)));
+
+        if (!stmt->match(pattern, &_matcher)) {
+            outerCallName = _builder.IdExpression();
+            binary = _builder.BinaryExpression();
+            // the expansion of Q_ASSERT(...),
+            // ((!(... == ...)) ? qt_assert(...) : ...);
+            pattern = _builder.ExpressionStatement(
+                        _builder.NestedExpression(
+                            _builder.ConditionalExpression(
+                                _builder.NestedExpression(
+                                    _builder.UnaryExpression(
+                                        _builder.NestedExpression(
+                                            binary))),
+                                _builder.Call(outerCallName))));
+
+            if (!stmt->match(pattern, &_matcher))
+                return null;
+        }
+
+        const QString outerCall = stringOf(outerCallName);
+        if (outerCall != QLatin1String("qt_assert")
+                && outerCall != QLatin1String("assert")
+                && outerCall != QLatin1String("Q_ASSERT"))
+            return null;
+
+        if (translationUnit()->tokenAt(binary->binary_op_token).kind() != T_EQUAL_EQUAL)
+            return null;
+
+        ExpressionAST *lhsExp = skipStringCall(binary->left_expression);
+        ExpressionAST *rhsExp = skipStringCall(binary->right_expression);
+        if (!lhsExp || !rhsExp)
+            return null;
+
+        StringLiteralAST *uriString = lhsExp->asStringLiteral();
+        IdExpressionAST *uriArgName = lhsExp->asIdExpression();
+        if (!uriString)
+            uriString = rhsExp->asStringLiteral();
+        if (!uriArgName)
+            uriArgName = rhsExp->asIdExpression();
+        if (!uriString || !uriArgName)
+            return null;
+
+        if (stringOf(uriArgName) != stringOf(uriName))
+            return null;
+
+        const StringLiteral *packageLit = translationUnit()->stringLiteral(uriString->literal_token);
+        return QString::fromUtf8(packageLit->chars(), packageLit->size());
+    }
+};
+
+static FullySpecifiedType stripPointerAndReference(const FullySpecifiedType &type)
+{
+    Type *t = type.type();
+    while (t) {
+        if (PointerType *ptr = t->asPointerType())
+            t = ptr->elementType().type();
+        else if (ReferenceType *ref = t->asReferenceType())
+            t = ref->elementType().type();
+        else
+            break;
+    }
+    return FullySpecifiedType(t);
+}
+
+static QString toQmlType(const FullySpecifiedType &type)
+{
+    Overview overview;
+    QString result = overview(stripPointerAndReference(type));
+    if (result == QLatin1String("QString"))
+        result = QLatin1String("string");
+    return result;
+}
+
+static Class *lookupClass(const QString &expression, Scope *scope, TypeOfExpression &typeOf)
+{
+    QList<LookupItem> results = typeOf(expression, scope);
+    Class *klass = 0;
+    foreach (const LookupItem &item, results) {
+        if (item.declaration()) {
+            klass = item.declaration()->asClass();
+            if (klass)
+                return klass;
+        }
+    }
+    return 0;
+}
+
+static void populate(LanguageUtils::FakeMetaObject::Ptr fmo, Class *klass,
+                     QHash<Class *, LanguageUtils::FakeMetaObject::Ptr> *classes,
+                     TypeOfExpression &typeOf)
+{
+    using namespace LanguageUtils;
+
+    Overview namePrinter;
+
+    classes->insert(klass, fmo);
+
+    for (unsigned i = 0; i < klass->memberCount(); ++i) {
+        Symbol *member = klass->memberAt(i);
+        if (!member->name())
+            continue;
+        if (Function *func = member->type()->asFunctionType()) {
+            if (!func->isSlot() && !func->isInvokable() && !func->isSignal())
+                continue;
+            FakeMetaMethod method(namePrinter(func->name()), toQmlType(func->returnType()));
+            if (func->isSignal())
+                method.setMethodType(FakeMetaMethod::Signal);
+            else
+                method.setMethodType(FakeMetaMethod::Slot);
+            for (unsigned a = 0; a < func->argumentCount(); ++a) {
+                Symbol *arg = func->argumentAt(a);
+                QString name;
+                if (arg->name())
+                    name = namePrinter(arg->name());
+                method.addParameter(name, toQmlType(arg->type()));
+            }
+            fmo->addMethod(method);
+        }
+        if (QtPropertyDeclaration *propDecl = member->asQtPropertyDeclaration()) {
+            const FullySpecifiedType &type = propDecl->type();
+            const bool isList = false; // ### fixme
+            const bool isWritable = propDecl->flags() & QtPropertyDeclaration::WriteFunction;
+            const bool isPointer = type.type() && type.type()->isPointerType();
+            const int revision = 0; // ### fixme
+            FakeMetaProperty property(
+                        namePrinter(propDecl->name()),
+                        toQmlType(type),
+                        isList, isWritable, isPointer,
+                        revision);
+            fmo->addProperty(property);
+        }
+        if (QtEnum *qtEnum = member->asQtEnum()) {
+            // find the matching enum
+            Enum *e = 0;
+            QList<LookupItem> result = typeOf(namePrinter(qtEnum->name()), klass);
+            foreach (const LookupItem &item, result) {
+                if (item.declaration()) {
+                    e = item.declaration()->asEnum();
+                    if (e)
+                        break;
+                }
+            }
+            if (!e)
+                continue;
+
+            FakeMetaEnum metaEnum(namePrinter(e->name()));
+            for (unsigned j = 0; j < e->memberCount(); ++j) {
+                Symbol *enumMember = e->memberAt(j);
+                if (!enumMember->name())
+                    continue;
+                metaEnum.addKey(namePrinter(enumMember->name()), 0);
+            }
+            fmo->addEnum(metaEnum);
+        }
+    }
+
+    // only single inheritance is supported
+    if (klass->baseClassCount() > 0) {
+        BaseClass *base = klass->baseClassAt(0);
+        if (!base->name())
+            return;
+
+        const QString baseClassName = namePrinter(base->name());
+        fmo->setSuperclassName(baseClassName);
+
+        Class *baseClass = lookupClass(baseClassName, klass, typeOf);
+        if (!baseClass)
+            return;
+
+        FakeMetaObject::Ptr baseFmo = classes->value(baseClass);
+        if (!baseFmo) {
+            baseFmo = FakeMetaObject::Ptr(new FakeMetaObject);
+            populate(baseFmo, baseClass, classes, typeOf);
+        }
+    }
+}
+
+QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(
+        const Document::Ptr &doc,
+        const Snapshot &snapshot,
+        const QList<ExportedQmlType> &exportedTypes)
+{
+    using namespace LanguageUtils;
+    QList<FakeMetaObject::ConstPtr> exportedObjects;
+    QHash<Class *, FakeMetaObject::Ptr> classes;
+
+    if (exportedTypes.isEmpty())
+        return exportedObjects;
+
+    TypeOfExpression typeOf;
+    typeOf.init(doc, snapshot);
+    foreach (const ExportedQmlType &exportedType, exportedTypes) {
+        FakeMetaObject::Ptr fmo(new FakeMetaObject);
+        fmo->addExport(exportedType.typeName,
+                       exportedType.packageName,
+                       exportedType.version);
+        exportedObjects += fmo;
+
+        Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
+        if (!klass)
+            continue;
+
+        // add the no-package export, so the cpp name can be used in properties
+        Overview overview;
+        fmo->addExport(overview(klass->name()), QmlJS::CppQmlTypes::cppPackage, ComponentVersion());
+
+        populate(fmo, klass, &classes, typeOf);
+    }
+
+    return exportedObjects;
+}
+
+} // anonymous namespace
+
+FindExportedCppTypes::FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot)
+    : m_snapshot(snapshot)
+{
+}
+
+QList<LanguageUtils::FakeMetaObject::ConstPtr> FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document)
+{
+    QList<LanguageUtils::FakeMetaObject::ConstPtr> noResults;
+    if (document->source().isEmpty()
+            || !document->translationUnit()->ast())
+        return noResults;
+
+    FindExportsVisitor finder(document);
+    QList<ExportedQmlType> exports = finder();
+    if (exports.isEmpty())
+        return noResults;
+
+    return exportedQmlObjects(document, m_snapshot, exports);
+}
+
+bool FindExportedCppTypes::maybeExportsTypes(const Document::Ptr &document)
+{
+    if (!document->control())
+        return false;
+    const QByteArray qmlRegisterTypeToken("qmlRegisterType");
+    if (document->control()->findIdentifier(
+                qmlRegisterTypeToken.constData(), qmlRegisterTypeToken.size())) {
+        return true;
+    }
+    return false;
+}
diff --git a/src/plugins/qmljstools/qmljsfindexportedcpptypes.h b/src/plugins/qmljstools/qmljsfindexportedcpptypes.h
new file mode 100644
index 00000000000..304894e9439
--- /dev/null
+++ b/src/plugins/qmljstools/qmljsfindexportedcpptypes.h
@@ -0,0 +1,55 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
+#define QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
+
+#include <cplusplus/CppDocument.h>
+#include <languageutils/fakemetaobject.h>
+
+namespace QmlJSTools {
+
+class FindExportedCppTypes
+{
+public:
+    FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot);
+    QList<LanguageUtils::FakeMetaObject::ConstPtr> operator()(const CPlusPlus::Document::Ptr &document);
+
+    static bool maybeExportsTypes(const CPlusPlus::Document::Ptr &document);
+
+private:
+    CPlusPlus::Snapshot m_snapshot;
+};
+
+} // namespace QmlJSTools
+
+#endif // QMLJSTOOLS_QMLJSFINDEXPORTEDCPPTYPES_H
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp
index 6ff2c895504..545761ad7e1 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp
@@ -33,6 +33,7 @@
 #include "qmljsmodelmanager.h"
 #include "qmljstoolsconstants.h"
 #include "qmljsplugindumper.h"
+#include "qmljsfindexportedcpptypes.h"
 
 #include <coreplugin/icore.h>
 #include <coreplugin/editormanager/editormanager.h>
@@ -41,8 +42,6 @@
 #include <coreplugin/messagemanager.h>
 #include <cplusplus/ModelManagerInterface.h>
 #include <cplusplus/CppDocument.h>
-#include <cplusplus/TypeOfExpression.h>
-#include <cplusplus/Overview.h>
 #include <qmljs/qmljscontext.h>
 #include <qmljs/qmljsbind.h>
 #include <qmljs/parser/qmldirparser_p.h>
@@ -98,8 +97,10 @@ void ModelManager::delayedInitialization()
     CPlusPlus::CppModelManagerInterface *cppModelManager =
             CPlusPlus::CppModelManagerInterface::instance();
     if (cppModelManager) {
+        // It's important to have a direct connection here so we can prevent
+        // the source and AST of the cpp document being cleaned away.
         connect(cppModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
-                this, SLOT(queueCppQmlTypeUpdate(CPlusPlus::Document::Ptr)));
+                this, SLOT(maybeQueueCppQmlTypeUpdate(CPlusPlus::Document::Ptr)), Qt::DirectConnection);
     }
 }
 
@@ -673,9 +674,25 @@ void ModelManager::loadPluginTypes(const QString &libraryPath, const QString &im
     m_pluginDumper->loadPluginTypes(libraryPath, importPath, importUri, importVersion);
 }
 
-void ModelManager::queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc)
+// is called *inside a c++ parsing thread*, to allow hanging on to source and ast
+void ModelManager::maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc)
 {
-    m_queuedCppDocuments.insert(doc->fileName());
+    // keep source and AST alive if we want to scan for register calls
+    const bool scan = FindExportedCppTypes::maybeExportsTypes(doc);
+    if (scan)
+        doc->keepSourceAndAST();
+
+    // delegate actual queuing to the gui thread
+    QMetaObject::invokeMethod(this, "queueCppQmlTypeUpdate",
+                              Q_ARG(CPlusPlus::Document::Ptr, doc), Q_ARG(bool, scan));
+}
+
+void ModelManager::queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan)
+{
+    QPair<CPlusPlus::Document::Ptr, bool> prev = m_queuedCppDocuments.value(doc->fileName());
+    if (prev.first && prev.second)
+        prev.first->releaseSourceAndAST();
+    m_queuedCppDocuments.insert(doc->fileName(), qMakePair(doc, scan));
     m_updateCppQmlTypesTimer->start();
 }
 
@@ -691,20 +708,32 @@ void ModelManager::startCppQmlTypeUpdate()
     m_queuedCppDocuments.clear();
 }
 
-void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager, CPlusPlus::CppModelManagerInterface *cppModelManager, QSet<QString> files)
+void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager,
+                                     CPlusPlus::CppModelManagerInterface *cppModelManager,
+                                     QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents)
 {
     CppQmlTypeHash newCppTypes = qmlModelManager->cppQmlTypes();
     CPlusPlus::Snapshot snapshot = cppModelManager->snapshot();
+    FindExportedCppTypes finder(snapshot);
+
+    typedef QPair<CPlusPlus::Document::Ptr, bool> DocScanPair;
+    foreach (const DocScanPair &pair, documents) {
+        CPlusPlus::Document::Ptr doc = pair.first;
+        const bool scan = pair.second;
+        const QString fileName = doc->fileName();
+        if (!scan) {
+            newCppTypes.remove(fileName);
+            continue;
+        }
+
+        QList<LanguageUtils::FakeMetaObject::ConstPtr> exported = finder(doc);
 
-    foreach (const QString &fileName, files) {
-        CPlusPlus::Document::Ptr doc = snapshot.document(fileName);
-        QList<LanguageUtils::FakeMetaObject::ConstPtr> exported;
-        if (doc)
-            exported = cppModelManager->exportedQmlObjects(doc);
         if (!exported.isEmpty())
             newCppTypes[fileName] = exported;
         else
             newCppTypes.remove(fileName);
+
+        doc->releaseSourceAndAST();
     }
 
     QMutexLocker locker(&qmlModelManager->m_cppTypesMutex);
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.h b/src/plugins/qmljstools/qmljsmodelmanager.h
index 7fe82145b38..0a8c26fdbd1 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.h
+++ b/src/plugins/qmljstools/qmljsmodelmanager.h
@@ -117,12 +117,15 @@ protected:
     void updateImportPaths();
 
 private slots:
-    void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
+    void maybeQueueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
+    void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc, bool scan);
     void startCppQmlTypeUpdate();
 
 private:
     static bool matchesMimeType(const Core::MimeType &fileMimeType, const Core::MimeType &knownMimeType);
-    static void updateCppQmlTypes(ModelManager *qmlModelManager, CPlusPlus::CppModelManagerInterface *cppModelManager, QSet<QString> files);
+    static void updateCppQmlTypes(ModelManager *qmlModelManager,
+                                  CPlusPlus::CppModelManagerInterface *cppModelManager,
+                                  QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > documents);
 
     mutable QMutex m_mutex;
     Core::ICore *m_core;
@@ -134,7 +137,7 @@ private:
     QFutureSynchronizer<void> m_synchronizer;
 
     QTimer *m_updateCppQmlTypesTimer;
-    QSet<QString> m_queuedCppDocuments;
+    QMap<QString, QPair<CPlusPlus::Document::Ptr, bool> > m_queuedCppDocuments;
     CppQmlTypeHash m_cppTypes;
     mutable QMutex m_cppTypesMutex;
 
diff --git a/src/plugins/qmljstools/qmljstools-lib.pri b/src/plugins/qmljstools/qmljstools-lib.pri
index 2d5cad5b231..596f28816e0 100644
--- a/src/plugins/qmljstools/qmljstools-lib.pri
+++ b/src/plugins/qmljstools/qmljstools-lib.pri
@@ -17,7 +17,8 @@ HEADERS += \
     $$PWD/qmljsfunctionfilter.h \
     $$PWD/qmljslocatordata.h \
     $$PWD/qmljsindenter.h \
-    $$PWD/qmljscodestylesettingspage.h
+    $$PWD/qmljscodestylesettingspage.h \
+    $$PWD/qmljsfindexportedcpptypes.h
 
 SOURCES += \
     $$PWD/qmljstoolsplugin.cpp \
@@ -30,7 +31,8 @@ SOURCES += \
     $$PWD/qmljsfunctionfilter.cpp \
     $$PWD/qmljslocatordata.cpp \
     $$PWD/qmljsindenter.cpp \
-    $$PWD/qmljscodestylesettingspage.cpp
+    $$PWD/qmljscodestylesettingspage.cpp \
+    $$PWD/qmljsfindexportedcpptypes.cpp
 
 FORMS += \
     $$PWD/qmljscodestylesettingspage.ui
-- 
GitLab