From 1e7bf7f7211fa082f207ffdfc8fd5d3556eae89b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 8 Jul 2016 13:09:56 +0200 Subject: [PATCH] QmlJS: Reparse cpp types when component headers change Previously the code model would only get updated when the document that contains the qmlRegisterType changed. If the actual component exported to QML lives in a different file, any updates to that component would be lost. With this change we keep track of the header files the components are declared in and rescan if any of them changes. This is still not the greatest way to do it as there are a number of ways to introduce dependencies the system cannot detect. It's better than before, though. Change-Id: Ic077c516dca3ac720f78973c84e5e6e91b6a5c07 Reviewed-by: Marco Benelli --- src/libs/qmljs/qmljsfindexportedcpptypes.cpp | 18 ++-- src/libs/qmljs/qmljsfindexportedcpptypes.h | 2 +- src/libs/qmljs/qmljsmodelmanagerinterface.cpp | 82 +++++++++++++------ src/libs/qmljs/qmljsmodelmanagerinterface.h | 1 + 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/src/libs/qmljs/qmljsfindexportedcpptypes.cpp b/src/libs/qmljs/qmljsfindexportedcpptypes.cpp index e03bf7d5274..ff82a56cd54 100644 --- a/src/libs/qmljs/qmljsfindexportedcpptypes.cpp +++ b/src/libs/qmljs/qmljsfindexportedcpptypes.cpp @@ -817,16 +817,17 @@ FindExportedCppTypes::FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot) { } -void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document) +QStringList FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document) { m_contextProperties.clear(); m_exportedTypes.clear(); + QStringList fileNames; // this check only guards against some input errors, if document's source and AST has not // been guarded properly the source and AST may still become empty/null while this function is running if (document->utf8Source().isEmpty() || !document->translationUnit()->ast()) - return; + return fileNames; FindExportsVisitor finder(document); finder(); @@ -838,7 +839,7 @@ void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document) const QList contextPropertyDescriptions = finder.contextProperties(); const QList exports = finder.exportedTypes(); if (exports.isEmpty() && contextPropertyDescriptions.isEmpty()) - return; + return fileNames; // context properties need lookup inside function scope, and thus require a full check CPlusPlus::Document::Ptr localDoc = document; @@ -863,14 +864,19 @@ void FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document) // convert to list of FakeMetaObject::ConstPtr m_exportedTypes.reserve(fakeMetaObjects.size() + extraFakeMetaObjects.size()); - foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, fakeMetaObjects) { - fmo->updateFingerprint(); - m_exportedTypes += fmo; + fileNames.reserve(fakeMetaObjects.size()); + for (auto it = fakeMetaObjects.constBegin(), end = fakeMetaObjects.constEnd(); it != end; + ++it) { + it.value()->updateFingerprint(); + m_exportedTypes += it.value(); + fileNames += QLatin1String(it.key()->fileName()); } foreach (const LanguageUtils::FakeMetaObject::Ptr &fmo, extraFakeMetaObjects) { fmo->updateFingerprint(); m_exportedTypes += fmo; } + + return fileNames; } QList FindExportedCppTypes::exportedTypes() const diff --git a/src/libs/qmljs/qmljsfindexportedcpptypes.h b/src/libs/qmljs/qmljsfindexportedcpptypes.h index fbc91268807..f626130c746 100644 --- a/src/libs/qmljs/qmljsfindexportedcpptypes.h +++ b/src/libs/qmljs/qmljsfindexportedcpptypes.h @@ -41,7 +41,7 @@ public: FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot); // document must have a valid source and ast for the duration of the call - void operator()(const CPlusPlus::Document::Ptr &document); + QStringList operator()(const CPlusPlus::Document::Ptr &document); QList exportedTypes() const; QHash contextProperties() const; diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp index f739668f9f7..a919087fea6 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp @@ -1259,6 +1259,37 @@ void ModelManagerInterface::asyncReset() m_asyncResetTimer->start(); } +bool rescanExports(const QString &fileName, FindExportedCppTypes &finder, + ModelManagerInterface::CppDataHash &newData) +{ + bool hasNewInfo = false; + + QList exported = finder.exportedTypes(); + QHash contextProperties = finder.contextProperties(); + if (exported.isEmpty() && contextProperties.isEmpty()) { + hasNewInfo = hasNewInfo || newData.remove(fileName) > 0; + } else { + ModelManagerInterface::CppData &data = newData[fileName]; + if (!hasNewInfo && (data.exportedTypes.size() != exported.size() + || data.contextProperties != contextProperties)) + hasNewInfo = true; + if (!hasNewInfo) { + QHash newFingerprints; + foreach (LanguageUtils::FakeMetaObject::ConstPtr newType, exported) + newFingerprints[newType->className()]=newType->fingerprint(); + foreach (LanguageUtils::FakeMetaObject::ConstPtr oldType, data.exportedTypes) { + if (newFingerprints.value(oldType->className()) != oldType->fingerprint()) { + hasNewInfo = true; + break; + } + } + } + data.exportedTypes = exported; + data.contextProperties = contextProperties; + } + return hasNewInfo; +} + void ModelManagerInterface::updateCppQmlTypes(QFutureInterface &interface, ModelManagerInterface *qmlModelManager, CPlusPlus::Snapshot snapshot, @@ -1266,7 +1297,14 @@ void ModelManagerInterface::updateCppQmlTypes(QFutureInterface &interface, { interface.setProgressRange(0, documents.size()); interface.setProgressValue(0); - CppDataHash newData = qmlModelManager->cppData(); + + CppDataHash newData; + QHash newDeclarations; + { + QMutexLocker locker(&qmlModelManager->m_cppDataMutex); + newData = qmlModelManager->m_cppDataHash; + newDeclarations = qmlModelManager->m_cppDeclarationFiles; + } FindExportedCppTypes finder(snapshot); @@ -1281,41 +1319,37 @@ void ModelManagerInterface::updateCppQmlTypes(QFutureInterface &interface, const bool scan = pair.second; const QString fileName = doc->fileName(); if (!scan) { - hasNewInfo = hasNewInfo || newData.remove(fileName) > 0; + hasNewInfo = newData.remove(fileName) > 0 || hasNewInfo; + foreach (const QString &file, newDeclarations[fileName]) { + finder(snapshot.document(file)); + hasNewInfo = rescanExports(file, finder, newData) || hasNewInfo; + } continue; } - finder(doc); - - QList exported = finder.exportedTypes(); - QHash contextProperties = finder.contextProperties(); - if (exported.isEmpty() && contextProperties.isEmpty()) { - hasNewInfo = hasNewInfo || newData.remove(fileName) > 0; - } else { - CppData &data = newData[fileName]; - if (!hasNewInfo && (data.exportedTypes.size() != exported.size() - || data.contextProperties != contextProperties)) - hasNewInfo = true; - if (!hasNewInfo) { - QHash newFingerprints; - foreach (LanguageUtils::FakeMetaObject::ConstPtr newType, exported) - newFingerprints[newType->className()]=newType->fingerprint(); - foreach (LanguageUtils::FakeMetaObject::ConstPtr oldType, data.exportedTypes) { - if (newFingerprints.value(oldType->className()) != oldType->fingerprint()) { - hasNewInfo = true; - break; - } + for (auto it = newDeclarations.begin(), end = newDeclarations.end(); it != end;) { + if (it->removeOne(fileName)) { + doc->releaseSourceAndAST(); + if (it->isEmpty()) { + it = newDeclarations.erase(it); + continue; } } - data.exportedTypes = exported; - data.contextProperties = contextProperties; + ++it; } + foreach (const QString &declarationFile, finder(doc)) { + newDeclarations[declarationFile].append(fileName); + doc->keepSourceAndAST(); // keep for later reparsing when dependent doc changes + } + + hasNewInfo = rescanExports(doc->fileName(), finder, newData) || hasNewInfo; doc->releaseSourceAndAST(); } QMutexLocker locker(&qmlModelManager->m_cppDataMutex); qmlModelManager->m_cppDataHash = newData; + qmlModelManager->m_cppDeclarationFiles = newDeclarations; if (hasNewInfo) // one could get away with re-linking the cpp types... QMetaObject::invokeMethod(qmlModelManager, "asyncReset"); diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h index 568fb63593c..3f640c6ca78 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.h +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h @@ -275,6 +275,7 @@ private: QrcCache m_qrcCache; CppDataHash m_cppDataHash; + QHash m_cppDeclarationFiles; mutable QMutex m_cppDataMutex; // project integration