From 0194da7300d3fd76594e337c785566c4498e12f4 Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Fri, 3 Dec 2010 10:13:15 +0100
Subject: [PATCH] Qml-C++: Find C++ qmlRegisterType calls and populate QML code
 model.

Reviewed-by: Erik Verbruggen
---
 src/libs/cplusplus/CppDocument.cpp            | 116 +++++++++++++
 src/libs/cplusplus/CppDocument.h              |  16 ++
 src/libs/cplusplus/ModelManagerInterface.h    |   3 +
 src/libs/qmljs/qmljsinterpreter.cpp           |   2 +
 src/libs/qmljs/qmljsinterpreter.h             |   1 +
 src/libs/qmljs/qmljslink.cpp                  |  19 +-
 src/plugins/cpptools/cppmodelmanager.cpp      | 162 +++++++++++++++++-
 src/plugins/cpptools/cppmodelmanager.h        |   2 +
 src/plugins/qmljstools/qmljsmodelmanager.cpp  |  30 ++++
 src/plugins/qmljstools/qmljsmodelmanager.h    |   8 +
 .../qmljstools/qmljstools_dependencies.pri    |   1 +
 src/plugins/qmljstools/qmljstoolsplugin.cpp   |   1 +
 12 files changed, 359 insertions(+), 2 deletions(-)

diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index ef159e93708..c2f6af3882e 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -585,6 +585,122 @@ void Document::check(CheckMode mode)
     }
 }
 
+class FindExposedQmlTypes : protected ASTVisitor
+{
+    Document *_doc;
+    QList<Document::ExportedQmlType> _exportedTypes;
+public:
+    FindExposedQmlTypes(Document *doc)
+        : ASTVisitor(doc->translationUnit())
+        , _doc(doc)
+    {}
+
+    QList<Document::ExportedQmlType> operator()()
+    {
+        _exportedTypes.clear();
+        accept(translationUnit()->ast());
+        return _exportedTypes;
+    }
+
+protected:
+    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;
+
+        // first and last arguments must be string literals
+        const StringLiteral *packageLit = 0;
+        const StringLiteral *nameLit = 0;
+        if (StringLiteralAST *packageAst = ast->expression_list->value->asStringLiteral())
+            packageLit = translationUnit()->stringLiteral(packageAst->literal_token);
+        if (StringLiteralAST *nameAst = ast->expression_list->next->next->next->value->asStringLiteral())
+            nameLit = translationUnit()->stringLiteral(nameAst->literal_token);
+        if (!nameLit) {
+            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;
+        }
+
+        // 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 (packageLit && majorLit && minorLit && majorLit->isInt() && minorLit->isInt()) {
+            exportedType.packageName = QString::fromUtf8(packageLit->chars(), packageLit->size());
+            exportedType.majorVersion = QString::fromUtf8(majorLit->chars(), majorLit->size()).toInt();
+            exportedType.minorVersion = QString::fromUtf8(minorLit->chars(), minorLit->size()).toInt();
+        } else {
+            translationUnit()->warning(ast->base_expression->firstToken(),
+                                       "The package will only be available in Qt Creator's QML editors when the package name is a string literal and\n"
+                                       "the versions are integer literals. The type will 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;
+    }
+};
+
+void Document::findExposedQmlTypes()
+{
+    if (! _translationUnit->ast())
+        return;
+
+    QByteArray token("qmlRegisterType");
+    if (! _translationUnit->control()->findIdentifier(token.constData(), token.size()))
+        return;
+
+    FindExposedQmlTypes finder(this);
+    _exportedQmlTypes = finder();
+}
+
 void Document::releaseSource()
 {
     _source.clear();
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index bbb7aaddb61..a00eeefa1d9 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -125,6 +125,8 @@ public:
 
     void check(CheckMode mode = FullCheck);
 
+    void findExposedQmlTypes();
+
     void releaseSource();
     void releaseTranslationUnit();
 
@@ -318,6 +320,19 @@ 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; }
+
 private:
     QString _fileName;
     Control *_control;
@@ -329,6 +344,7 @@ private:
     QList<Block> _skippedBlocks;
     QList<MacroUse> _macroUses;
     QList<UndefinedMacroUse> _undefinedMacroUses;
+    QList<ExportedQmlType> _exportedQmlTypes;
     QByteArray _source;
     QDateTime _lastModified;
     unsigned _revision;
diff --git a/src/libs/cplusplus/ModelManagerInterface.h b/src/libs/cplusplus/ModelManagerInterface.h
index c58925e6134..0a1f3217c4c 100644
--- a/src/libs/cplusplus/ModelManagerInterface.h
+++ b/src/libs/cplusplus/ModelManagerInterface.h
@@ -35,6 +35,7 @@
 #define CPPMODELMANAGERINTERFACE_H
 
 #include <cplusplus/CppDocument.h>
+#include <languageutils/fakemetaobject.h>
 #include <QtCore/QObject>
 #include <QtCore/QHash>
 #include <QtCore/QPointer>
@@ -146,6 +147,8 @@ public:
 
     virtual void findMacroUsages(const CPlusPlus::Macro &macro) = 0;
 
+    virtual QList<LanguageUtils::FakeMetaObject *> exportedQmlObjects() const = 0;
+
 Q_SIGNALS:
     void documentUpdated(CPlusPlus::Document::Ptr doc);
 
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index e4a0d785c2e..da0616cf0a4 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -1947,6 +1947,7 @@ const Value *Function::invoke(const Activation *activation) const
 ////////////////////////////////////////////////////////////////////////////////
 
 QList<const FakeMetaObject *> CppQmlTypesLoader::builtinObjects;
+QList<const FakeMetaObject *> CppQmlTypesLoader::cppObjects;
 
 QStringList CppQmlTypesLoader::load(const QFileInfoList &xmlFiles)
 {
@@ -2440,6 +2441,7 @@ Engine::Engine()
     initializePrototypes();
 
     _cppQmlTypes.load(this, CppQmlTypesLoader::builtinObjects);
+    _cppQmlTypes.load(this, CppQmlTypesLoader::cppObjects);
 
     // the 'Qt' object is dumped even though it is not exported
     // it contains useful information, in particular on enums - add the
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 60fbcababaa..758ca27cce4 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -593,6 +593,7 @@ public:
     /** \return an empty list when successful, error messages otherwise. */
     static QStringList load(const QFileInfoList &xmlFiles);
     static QList<const LanguageUtils::FakeMetaObject *> builtinObjects;
+    static QList<const LanguageUtils::FakeMetaObject *> cppObjects;
 
     // parses the xml string and fills the newObjects map
     static QString parseQmlTypeXml(const QByteArray &xml,
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index d36dbbb0218..0c4f85a1747 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -162,9 +162,26 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc)
 {
     Q_D(Link);
 
-    if (! (doc->qmlProgram() && doc->qmlProgram()->imports))
+    if (! doc->qmlProgram())
         return;
 
+    // implicit imports: the <default> package is always available
+    const QLatin1String defaultPackage("<default>");
+    if (engine()->cppQmlTypes().hasPackage(defaultPackage)) {
+        ImportInfo info(ImportInfo::LibraryImport, defaultPackage);
+        ObjectValue *import = d->importCache.value(ImportCacheKey(info));
+        if (!import) {
+            import = new ObjectValue(engine());
+            foreach (QmlObjectValue *object,
+                     engine()->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) {
+                import->setProperty(object->className(), object);
+            }
+            d->importCache.insert(ImportCacheKey(info), import);
+        }
+        typeEnv->addImport(import, info);
+    }
+
+
     // implicit imports:
     // qml files in the same directory are available without explicit imports
     ImportInfo implcitImportInfo(ImportInfo::ImplicitDirectoryImport, doc->path());
diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp
index efe8175dcbc..19d3530346f 100644
--- a/src/plugins/cpptools/cppmodelmanager.cpp
+++ b/src/plugins/cpptools/cppmodelmanager.cpp
@@ -77,6 +77,7 @@
 #include <Token.h>
 #include <Parser.h>
 #include <Control.h>
+#include <CoreTypes.h>
 
 #include <QtCore/QCoreApplication>
 #include <QtCore/QDebug>
@@ -290,6 +291,8 @@ public:
     void operator()()
     {
         _doc->check(_mode);
+        _doc->findExposedQmlTypes();
+        _doc->releaseSource();
         _doc->releaseTranslationUnit();
 
         if (_mode == Document::FastCheck)
@@ -589,7 +592,6 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
 
     doc->setSource(preprocessedCode);
     doc->tokenize();
-    doc->releaseSource();
 
     snapshot.insert(doc);
     m_todo.remove(fileName);
@@ -601,6 +603,7 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
 
     (void) switchDocument(previousDoc);
 #else
+    doc->releaseSource();
     Document::CheckMode mode = Document::FastCheck;
     mode = Document::FullCheck;
     doc->parse();
@@ -1418,5 +1421,162 @@ 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 *fmo, Class *klass,
+                     QHash<Class *, LanguageUtils::FakeMetaObject *> *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();
+            FakeMetaProperty property(
+                        namePrinter(propDecl->name()),
+                        toQmlType(type),
+                        isList, isWritable, isPointer);
+            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 *baseFmo = classes->value(baseClass);
+        if (!baseFmo) {
+            baseFmo = new FakeMetaObject;
+            populate(baseFmo, baseClass, classes, typeOf);
+        }
+        fmo->setSuperclass(baseFmo);
+    }
+}
+
+QList<LanguageUtils::FakeMetaObject *> CppModelManager::exportedQmlObjects() const
+{
+    using namespace LanguageUtils;
+    QList<FakeMetaObject *> exportedObjects;
+    QHash<Class *, FakeMetaObject *> classes;
+
+    const Snapshot currentSnapshot = snapshot();
+    foreach (Document::Ptr doc, currentSnapshot) {
+        TypeOfExpression typeOf;
+        typeOf.init(doc, currentSnapshot);
+        foreach (const Document::ExportedQmlType &exportedType, doc->exportedQmlTypes()) {
+            FakeMetaObject *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;
+}
+
 #endif
 
diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h
index 865421a16e5..bd0bef09622 100644
--- a/src/plugins/cpptools/cppmodelmanager.h
+++ b/src/plugins/cpptools/cppmodelmanager.h
@@ -131,6 +131,8 @@ public:
 
     virtual void findMacroUsages(const CPlusPlus::Macro &macro);
 
+    virtual QList<LanguageUtils::FakeMetaObject *> exportedQmlObjects() const;
+
     void setHeaderSuffixes(const QStringList &suffixes)
     { m_headerSuffixes = suffixes; }
 
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp
index 69f43c5579b..0477f9ed01c 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp
@@ -39,6 +39,7 @@
 #include <coreplugin/editormanager/editormanager.h>
 #include <coreplugin/progressmanager/progressmanager.h>
 #include <coreplugin/mimedatabase.h>
+#include <cplusplus/ModelManagerInterface.h>
 #include <qmljs/qmljsinterpreter.h>
 #include <qmljs/qmljsbind.h>
 #include <qmljs/parser/qmldirparser_p.h>
@@ -56,6 +57,7 @@
 #include <qtconcurrent/runextensions.h>
 #include <QTextStream>
 #include <QCoreApplication>
+#include <QTimer>
 
 #include <QDebug>
 
@@ -72,6 +74,11 @@ ModelManager::ModelManager(QObject *parent):
 {
     m_synchronizer.setCancelOnWait(true);
 
+    m_updateCppQmlTypesTimer = new QTimer(this);
+    m_updateCppQmlTypesTimer->setInterval(1000);
+    m_updateCppQmlTypesTimer->setSingleShot(true);
+    connect(m_updateCppQmlTypesTimer, SIGNAL(timeout()), SLOT(updateCppQmlTypes()));
+
     qRegisterMetaType<QmlJS::Document::Ptr>("QmlJS::Document::Ptr");
     qRegisterMetaType<QmlJS::LibraryInfo>("QmlJS::LibraryInfo");
 
@@ -81,6 +88,16 @@ ModelManager::ModelManager(QObject *parent):
     updateImportPaths();
 }
 
+void ModelManager::delayedInitialization()
+{
+    CPlusPlus::CppModelManagerInterface *cppModelManager =
+            CPlusPlus::CppModelManagerInterface::instance();
+    if (cppModelManager) {
+        connect(cppModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
+                m_updateCppQmlTypesTimer, SLOT(start()));
+    }
+}
+
 void ModelManager::loadQmlTypeDescriptions()
 {
     if (Core::ICore::instance()) {
@@ -537,3 +554,16 @@ void ModelManager::loadPluginTypes(const QString &libraryPath, const QString &im
 {
     m_pluginDumper->loadPluginTypes(libraryPath, importPath, importUri);
 }
+
+void ModelManager::updateCppQmlTypes()
+{
+    CPlusPlus::CppModelManagerInterface *cppModelManager =
+            CPlusPlus::CppModelManagerInterface::instance();
+    if (!cppModelManager)
+        return;
+
+    QList<const LanguageUtils::FakeMetaObject *> constFMOs;
+    foreach (LanguageUtils::FakeMetaObject *fmo, cppModelManager->exportedQmlObjects())
+        constFMOs.append(fmo);
+    Interpreter::CppQmlTypesLoader::cppObjects = constFMOs;
+}
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.h b/src/plugins/qmljstools/qmljsmodelmanager.h
index a80f18cdca3..1a0b4c2833d 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.h
+++ b/src/plugins/qmljstools/qmljsmodelmanager.h
@@ -44,6 +44,8 @@
 #include <QMutex>
 #include <QProcess>
 
+QT_FORWARD_DECLARE_CLASS(QTimer)
+
 namespace Core {
 class ICore;
 class MimeType;
@@ -61,6 +63,8 @@ class QMLJSTOOLS_EXPORT ModelManager: public QmlJS::ModelManagerInterface
 public:
     ModelManager(QObject *parent = 0);
 
+    void delayedInitialization();
+
     virtual WorkingCopy workingCopy() const;
     virtual QmlJS::Snapshot snapshot() const;
 
@@ -99,6 +103,9 @@ protected:
 
     void updateImportPaths();
 
+private slots:
+    void updateCppQmlTypes();
+
 private:
     static bool matchesMimeType(const Core::MimeType &fileMimeType, const Core::MimeType &knownMimeType);
 
@@ -109,6 +116,7 @@ private:
     QStringList m_defaultImportPaths;
 
     QFutureSynchronizer<void> m_synchronizer;
+    QTimer *m_updateCppQmlTypesTimer;
 
     // project integration
     QMap<ProjectExplorer::Project *, ProjectInfo> m_projects;
diff --git a/src/plugins/qmljstools/qmljstools_dependencies.pri b/src/plugins/qmljstools/qmljstools_dependencies.pri
index d63c784082d..c94524e75c6 100644
--- a/src/plugins/qmljstools/qmljstools_dependencies.pri
+++ b/src/plugins/qmljstools/qmljstools_dependencies.pri
@@ -1,4 +1,5 @@
 include($$IDE_SOURCE_TREE/src/libs/languageutils/languageutils.pri)
+include($$IDE_SOURCE_TREE/src/libs/cplusplus/cplusplus.pri)
 include($$IDE_SOURCE_TREE/src/libs/qmljs/qmljs.pri)
 include($$IDE_SOURCE_TREE/src/plugins/projectexplorer/projectexplorer.pri)
 include($$IDE_SOURCE_TREE/src/plugins/texteditor/texteditor.pri)
diff --git a/src/plugins/qmljstools/qmljstoolsplugin.cpp b/src/plugins/qmljstools/qmljstoolsplugin.cpp
index c1f0e91e6b1..3a456eed752 100644
--- a/src/plugins/qmljstools/qmljstoolsplugin.cpp
+++ b/src/plugins/qmljstools/qmljstoolsplugin.cpp
@@ -89,6 +89,7 @@ bool QmlJSToolsPlugin::initialize(const QStringList &arguments, QString *error)
 
 void QmlJSToolsPlugin::extensionsInitialized()
 {
+    m_modelManager->delayedInitialization();
 }
 
 ExtensionSystem::IPlugin::ShutdownFlag QmlJSToolsPlugin::aboutToShutdown()
-- 
GitLab