diff options
Diffstat (limited to 'src/qmldom/qqmldomtop_p.h')
-rw-r--r-- | src/qmldom/qqmldomtop_p.h | 730 |
1 files changed, 528 insertions, 202 deletions
diff --git a/src/qmldom/qqmldomtop_p.h b/src/qmldom/qqmldomtop_p.h index f73a973906..afdea6b311 100644 --- a/src/qmldom/qqmldomtop_p.h +++ b/src/qmldom/qqmldomtop_p.h @@ -31,43 +31,21 @@ QT_BEGIN_NAMESPACE +using namespace Qt::Literals::StringLiterals; + namespace QQmlJS { namespace Dom { -class QMLDOM_EXPORT ParsingTask { -public: - QCborMap toCbor() const { - return QCborMap( - {{ QString::fromUtf16(Fields::requestedAt), QCborValue(requestedAt)}, - { QString::fromUtf16(Fields::loadOptions), int(loadOptions)}, - { QString::fromUtf16(Fields::kind), int(kind)}, - { QString::fromUtf16(Fields::canonicalPath), canonicalPath}, - { QString::fromUtf16(Fields::logicalPath), logicalPath}, - { QString::fromUtf16(Fields::contents), contents}, - { QString::fromUtf16(Fields::contentsDate), QCborValue(contentsDate)}, - { QString::fromUtf16(Fields::hasCallback), bool(callback)}}); - } - - QDateTime requestedAt; - LoadOptions loadOptions; - DomType kind; - QString canonicalPath; - QString logicalPath; - QString contents; - QDateTime contentsDate; - std::weak_ptr<DomUniverse> requestingUniverse; // make it a shared_ptr? - function<void(Path, DomItem &, DomItem &)> callback; -}; - class QMLDOM_EXPORT ExternalItemPairBase: public OwningItem { // all access should have the lock of the DomUniverse containing this Q_DECLARE_TR_FUNCTIONS(ExternalItemPairBase); public: constexpr static DomType kindValue = DomType::ExternalItemPair; DomType kind() const final override { return kindValue; } - ExternalItemPairBase(QDateTime validExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), - QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), - int derivedFrom = 0, - QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) + ExternalItemPairBase( + const QDateTime &validExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), + const QDateTime ¤tExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), + int derivedFrom = 0, + const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) : OwningItem(derivedFrom, lastDataUpdateAt), validExposedAt(validExposedAt), currentExposedAt(currentExposedAt) @@ -76,21 +54,21 @@ public: OwningItem(o), validExposedAt(o.validExposedAt), currentExposedAt(o.currentExposedAt) {} virtual std::shared_ptr<ExternalOwningItem> validItem() const = 0; - virtual DomItem validItem(DomItem &self) const = 0; + virtual DomItem validItem(const DomItem &self) const = 0; virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0; - virtual DomItem currentItem(DomItem &self) const = 0; + virtual DomItem currentItem(const DomItem &self) const = 0; - QString canonicalFilePath(DomItem &) const final override; - Path canonicalPath(DomItem &self) const final override; - bool iterateDirectSubpaths(DomItem &self, DirectVisitor) final override; - DomItem field(DomItem &self, QStringView name) const final override + QString canonicalFilePath(const DomItem &) const final override; + Path canonicalPath(const DomItem &self) const final override; + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const final override; + DomItem field(const DomItem &self, QStringView name) const final override { return OwningItem::field(self, name); } bool currentIsValid() const; - std::shared_ptr<ExternalItemPairBase> makeCopy(DomItem &self) const + std::shared_ptr<ExternalItemPairBase> makeCopy(const DomItem &self) const { return std::static_pointer_cast<ExternalItemPairBase>(doCopy(self)); } @@ -104,9 +82,9 @@ public: void refreshedDataAt(QDateTime tNew) final override { - return OwningItem::refreshedDataAt(tNew); if (currentItem()) currentItem()->refreshedDataAt(tNew); + return OwningItem::refreshedDataAt(tNew); } friend class DomUniverse; @@ -119,7 +97,7 @@ template<class T> class QMLDOM_EXPORT ExternalItemPair final : public ExternalItemPairBase { // all access should have the lock of the DomUniverse containing this protected: - std::shared_ptr<OwningItem> doCopy(DomItem &) const override + std::shared_ptr<OwningItem> doCopy(const DomItem &) const override { return std::make_shared<ExternalItemPair>(*this); } @@ -127,11 +105,12 @@ protected: public: constexpr static DomType kindValue = DomType::ExternalItemPair; friend class DomUniverse; - ExternalItemPair(std::shared_ptr<T> valid = {}, std::shared_ptr<T> current = {}, - QDateTime validExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), - QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), - int derivedFrom = 0, - QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) + ExternalItemPair( + const std::shared_ptr<T> &valid = {}, const std::shared_ptr<T> ¤t = {}, + const QDateTime &validExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), + const QDateTime ¤tExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), + int derivedFrom = 0, + const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) : ExternalItemPairBase(validExposedAt, currentExposedAt, derivedFrom, lastDataUpdateAt), valid(valid), current(current) @@ -141,10 +120,10 @@ public: { } std::shared_ptr<ExternalOwningItem> validItem() const override { return valid; } - DomItem validItem(DomItem &self) const override { return self.copy(valid); } + DomItem validItem(const DomItem &self) const override { return self.copy(valid); } std::shared_ptr<ExternalOwningItem> currentItem() const override { return current; } - DomItem currentItem(DomItem &self) const override { return self.copy(current); } - std::shared_ptr<ExternalItemPair> makeCopy(DomItem &self) const + DomItem currentItem(const DomItem &self) const override { return self.copy(current); } + std::shared_ptr<ExternalItemPair> makeCopy(const DomItem &self) const { return std::static_pointer_cast<ExternalItemPair>(doCopy(self)); } @@ -171,11 +150,11 @@ public: virtual Path canonicalPath() const = 0; - Path canonicalPath(DomItem &) const override; - DomItem containingObject(DomItem &) const override; - bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; + Path canonicalPath(const DomItem &) const override; + DomItem containingObject(const DomItem &) const override; + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override; template<typename T> - void setExtraOwningItem(QString fieldName, std::shared_ptr<T> item) + void setExtraOwningItem(const QString &fieldName, const std::shared_ptr<T> &item) { QMutexLocker l(mutex()); if (!item) @@ -191,55 +170,55 @@ private: QMap<QString, OwnerT> m_extraOwningItems; }; -class QMLDOM_EXPORT DomUniverse final : public DomTop +class QMLDOM_EXPORT DomUniverse final : public DomTop, + public std::enable_shared_from_this<DomUniverse> { Q_GADGET Q_DECLARE_TR_FUNCTIONS(DomUniverse); protected: - std::shared_ptr<OwningItem> doCopy(DomItem &self) const override; + std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override; public: - enum class Option{ - Default, - SingleThreaded - }; - Q_ENUM(Option) - Q_DECLARE_FLAGS(Options, Option); constexpr static DomType kindValue = DomType::DomUniverse; DomType kind() const override { return kindValue; } static ErrorGroups myErrors(); - DomUniverse(QString universeName, Options options = Option::SingleThreaded); + DomUniverse(const QString &universeName); DomUniverse(const DomUniverse &) = delete; - static std::shared_ptr<DomUniverse> guaranteeUniverse(std::shared_ptr<DomUniverse> univ); - static DomItem create(QString universeName, Options options = Option::SingleThreaded); + static std::shared_ptr<DomUniverse> guaranteeUniverse(const std::shared_ptr<DomUniverse> &univ); + static DomItem create(const QString &universeName); Path canonicalPath() const override; using DomTop::canonicalPath; - bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; - std::shared_ptr<DomUniverse> makeCopy(DomItem &self) const + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override; + std::shared_ptr<DomUniverse> makeCopy(const DomItem &self) const { return std::static_pointer_cast<DomUniverse>(doCopy(self)); } - void loadFile(DomItem &self, QString filePath, QString logicalPath, Callback callback, - LoadOptions loadOptions, - std::optional<DomType> fileType = std::optional<DomType>()); - void loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath, QString code, - QDateTime codeDate, Callback callback, LoadOptions loadOptions, - std::optional<DomType> fileType = std::optional<DomType>()); - void execQueue(); + // Helper structure reflecting the change in the map once loading && parsing is completed + // formerItem - DomItem representing value (ExternalItemPair) existing in the map before the + // loading && parsing. Might be empty (if didn't exist / failure) or equal to currentItem + // currentItem - DomItem representing current map value + struct LoadResult + { + DomItem formerItem; + DomItem currentItem; + }; + + LoadResult loadFile(const FileToLoad &file, DomType fileType, + DomCreationOptions creationOptions = {}); void removePath(const QString &dir); - std::shared_ptr<ExternalItemPair<GlobalScope>> globalScopeWithName(QString name) const + std::shared_ptr<ExternalItemPair<GlobalScope>> globalScopeWithName(const QString &name) const { QMutexLocker l(mutex()); return m_globalScopeWithName.value(name); } - std::shared_ptr<ExternalItemPair<GlobalScope>> ensureGlobalScopeWithName(QString name) + std::shared_ptr<ExternalItemPair<GlobalScope>> ensureGlobalScopeWithName(const QString &name) { if (auto current = globalScopeWithName(name)) return current; @@ -263,7 +242,7 @@ public: return QSet<QString>(map.keyBegin(), map.keyEnd()); } - std::shared_ptr<ExternalItemPair<QmlDirectory>> qmlDirectoryWithPath(QString path) const + std::shared_ptr<ExternalItemPair<QmlDirectory>> qmlDirectoryWithPath(const QString &path) const { QMutexLocker l(mutex()); return m_qmlDirectoryWithPath.value(path); @@ -278,7 +257,7 @@ public: return QSet<QString>(map.keyBegin(), map.keyEnd()); } - std::shared_ptr<ExternalItemPair<QmldirFile>> qmldirFileWithPath(QString path) const + std::shared_ptr<ExternalItemPair<QmldirFile>> qmldirFileWithPath(const QString &path) const { QMutexLocker l(mutex()); return m_qmldirFileWithPath.value(path); @@ -293,7 +272,7 @@ public: return QSet<QString>(map.keyBegin(), map.keyEnd()); } - std::shared_ptr<ExternalItemPair<QmlFile>> qmlFileWithPath(QString path) const + std::shared_ptr<ExternalItemPair<QmlFile>> qmlFileWithPath(const QString &path) const { QMutexLocker l(mutex()); return m_qmlFileWithPath.value(path); @@ -308,7 +287,7 @@ public: return QSet<QString>(map.keyBegin(), map.keyEnd()); } - std::shared_ptr<ExternalItemPair<JsFile>> jsFileWithPath(QString path) const + std::shared_ptr<ExternalItemPair<JsFile>> jsFileWithPath(const QString &path) const { QMutexLocker l(mutex()); return m_jsFileWithPath.value(path); @@ -323,7 +302,7 @@ public: return QSet<QString>(map.keyBegin(), map.keyEnd()); } - std::shared_ptr<ExternalItemPair<QmltypesFile>> qmltypesFileWithPath(QString path) const + std::shared_ptr<ExternalItemPair<QmltypesFile>> qmltypesFileWithPath(const QString &path) const { QMutexLocker l(mutex()); return m_qmltypesFileWithPath.value(path); @@ -341,37 +320,145 @@ public: QString name() const { return m_name; } - Options options() const { - return m_options; + +private: + struct ContentWithDate + { + QString content; + QDateTime date; + }; + // contains either Content with the timestamp when it was read or an Error + using ReadResult = std::variant<ContentWithDate, ErrorMessage>; + ReadResult readFileContent(const QString &canonicalPath) const; + + LoadResult load(const ContentWithDate &codeWithDate, const FileToLoad &file, DomType fType, + DomCreationOptions creationOptions = {}); + + // contains either Content to be parsed or LoadResult if loading / parsing is not needed + using PreloadResult = std::variant<ContentWithDate, LoadResult>; + PreloadResult preload(const DomItem &univ, const FileToLoad &file, DomType fType) const; + + std::shared_ptr<QmlFile> parseQmlFile(const QString &code, const FileToLoad &file, + const QDateTime &contentDate, + DomCreationOptions creationOptions); + std::shared_ptr<JsFile> parseJsFile(const QString &code, const FileToLoad &file, + const QDateTime &contentDate); + std::shared_ptr<ExternalItemPairBase> getPathValueOrNull(DomType fType, + const QString &path) const; + std::optional<DomItem> getItemIfMostRecent(const DomItem &univ, DomType fType, + const QString &path) const; + std::optional<DomItem> getItemIfHasSameCode(const DomItem &univ, DomType fType, + const QString &canonicalPath, + const ContentWithDate &codeWithDate) const; + static bool valueHasMostRecentItem(const ExternalItemPairBase *value, + const QDateTime &lastModified); + static bool valueHasSameContent(const ExternalItemPairBase *value, const QString &content); + + // TODO better name / consider proper public get/set + template <typename T> + QMap<QString, std::shared_ptr<ExternalItemPair<T>>> &getMutableRefToMap() + { + Q_ASSERT(!mutex()->tryLock()); + if constexpr (std::is_same_v<T, QmlDirectory>) { + return m_qmlDirectoryWithPath; + } + if constexpr (std::is_same_v<T, QmldirFile>) { + return m_qmldirFileWithPath; + } + if constexpr (std::is_same_v<T, QmlFile>) { + return m_qmlFileWithPath; + } + if constexpr (std::is_same_v<T, JsFile>) { + return m_jsFileWithPath; + } + if constexpr (std::is_same_v<T, QmltypesFile>) { + return m_qmltypesFileWithPath; + } + if constexpr (std::is_same_v<T, GlobalScope>) { + return m_globalScopeWithName; + } + Q_UNREACHABLE(); } - QQueue<ParsingTask> queue() const { - QMutexLocker l(mutex()); - return m_queue; + + // Inserts or updates an entry reflecting ExternalItem in the corresponding map + // Returns a pair of: + // - current ExternalItemPair, current value in the map (might be empty, or equal to curValue) + // - new current ExternalItemPair, value in the map after after the execution of this function + template <typename T> + QPair<std::shared_ptr<ExternalItemPair<T>>, std::shared_ptr<ExternalItemPair<T>>> + insertOrUpdateEntry(std::shared_ptr<T> newItem) + { + std::shared_ptr<ExternalItemPair<T>> curValue; + std::shared_ptr<ExternalItemPair<T>> newCurValue; + QString canonicalPath = newItem->canonicalFilePath(); + QDateTime now = QDateTime::currentDateTimeUtc(); + { + QMutexLocker l(mutex()); + auto &map = getMutableRefToMap<T>(); + auto it = map.find(canonicalPath); + if (it != map.cend() && (*it) && (*it)->current) { + curValue = *it; + if (valueHasSameContent(curValue.get(), newItem->code())) { + // value in the map has same content as newItem, a.k.a. most recent + newCurValue = curValue; + if (newCurValue->current->lastDataUpdateAt() < newItem->lastDataUpdateAt()) { + // update timestamp in the current, as if its content was refreshed by + // NewItem + newCurValue->current->refreshedDataAt(newItem->lastDataUpdateAt()); + } + } else if (curValue->current->lastDataUpdateAt() > newItem->lastDataUpdateAt()) { + // value in the map is more recent than newItem, nothing to update + newCurValue = curValue; + } else { + // perform update with newItem + curValue->current = std::move(newItem); + curValue->currentExposedAt = now; + if (curValue->current->isValid()) { + curValue->valid = curValue->current; + curValue->validExposedAt = std::move(now); + } + newCurValue = curValue; + } + } else { + // not found / invalid, just insert + newCurValue = std::make_shared<ExternalItemPair<T>>( + (newItem->isValid() ? newItem : std::shared_ptr<T>()), newItem, now, now); + map.insert(canonicalPath, newCurValue); + } + } + return qMakePair(curValue, newCurValue); + } + + // Inserts or updates an entry reflecting ExternalItem in the corresponding map + // returns LoadResult reflecting the change made to the map + template <typename T> + LoadResult insertOrUpdateExternalItem(std::shared_ptr<T> extItem) + { + auto change = insertOrUpdateEntry<T>(std::move(extItem)); + DomItem univ(shared_from_this()); + return { univ.copy(change.first), univ.copy(change.second) }; } private: QString m_name; - Options m_options; QMap<QString, std::shared_ptr<ExternalItemPair<GlobalScope>>> m_globalScopeWithName; QMap<QString, std::shared_ptr<ExternalItemPair<QmlDirectory>>> m_qmlDirectoryWithPath; QMap<QString, std::shared_ptr<ExternalItemPair<QmldirFile>>> m_qmldirFileWithPath; QMap<QString, std::shared_ptr<ExternalItemPair<QmlFile>>> m_qmlFileWithPath; QMap<QString, std::shared_ptr<ExternalItemPair<JsFile>>> m_jsFileWithPath; QMap<QString, std::shared_ptr<ExternalItemPair<QmltypesFile>>> m_qmltypesFileWithPath; - QQueue<ParsingTask> m_queue; }; - Q_DECLARE_OPERATORS_FOR_FLAGS(DomUniverse::Options) - class QMLDOM_EXPORT ExternalItemInfoBase: public OwningItem { Q_DECLARE_TR_FUNCTIONS(ExternalItemInfoBase); public: constexpr static DomType kindValue = DomType::ExternalItemInfo; DomType kind() const final override { return kindValue; } - ExternalItemInfoBase(Path canonicalPath, - QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), - int derivedFrom = 0, - QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) + ExternalItemInfoBase( + const Path &canonicalPath, + const QDateTime ¤tExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), + int derivedFrom = 0, + const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) : OwningItem(derivedFrom, lastDataUpdateAt), m_canonicalPath(canonicalPath), m_currentExposedAt(currentExposedAt) @@ -379,22 +466,22 @@ public: ExternalItemInfoBase(const ExternalItemInfoBase &o) = default; virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0; - virtual DomItem currentItem(DomItem &) const = 0; + virtual DomItem currentItem(const DomItem &) const = 0; - QString canonicalFilePath(DomItem &) const final override; + QString canonicalFilePath(const DomItem &) const final override; Path canonicalPath() const { return m_canonicalPath; } - Path canonicalPath(DomItem &) const final override { return canonicalPath(); } - bool iterateDirectSubpaths(DomItem &self, DirectVisitor) final override; - DomItem field(DomItem &self, QStringView name) const final override + Path canonicalPath(const DomItem &) const final override { return canonicalPath(); } + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const final override; + DomItem field(const DomItem &self, QStringView name) const final override { return OwningItem::field(self, name); } - int currentRevision(DomItem &self) const; - int lastRevision(DomItem &self) const; - int lastValidRevision(DomItem &self) const; + int currentRevision(const DomItem &self) const; + int lastRevision(const DomItem &self) const; + int lastValidRevision(const DomItem &self) const; - std::shared_ptr<ExternalItemInfoBase> makeCopy(DomItem &self) const + std::shared_ptr<ExternalItemInfoBase> makeCopy(const DomItem &self) const { return std::static_pointer_cast<ExternalItemInfoBase>(doCopy(self)); } @@ -408,12 +495,12 @@ public: void refreshedDataAt(QDateTime tNew) final override { - return OwningItem::refreshedDataAt(tNew); if (currentItem()) currentItem()->refreshedDataAt(tNew); + return OwningItem::refreshedDataAt(tNew); } - void ensureLogicalFilePath(QString path) { + void ensureLogicalFilePath(const QString &path) { QMutexLocker l(mutex()); if (!m_logicalFilePaths.contains(path)) m_logicalFilePaths.append(path); @@ -446,28 +533,29 @@ template<typename T> class ExternalItemInfo final : public ExternalItemInfoBase { protected: - std::shared_ptr<OwningItem> doCopy(DomItem &) const override + std::shared_ptr<OwningItem> doCopy(const DomItem &) const override { return std::make_shared<ExternalItemInfo>(*this); } public: constexpr static DomType kindValue = DomType::ExternalItemInfo; - ExternalItemInfo(std::shared_ptr<T> current = std::shared_ptr<T>(), - QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), - int derivedFrom = 0, - QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) + ExternalItemInfo( + const std::shared_ptr<T> ¤t = std::shared_ptr<T>(), + const QDateTime ¤tExposedAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC), + int derivedFrom = 0, + const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) : ExternalItemInfoBase(current->canonicalPath().dropTail(), currentExposedAt, derivedFrom, lastDataUpdateAt), current(current) {} - ExternalItemInfo(QString canonicalPath) : current(new T(canonicalPath)) { } + ExternalItemInfo(const QString &canonicalPath) : current(new T(canonicalPath)) { } ExternalItemInfo(const ExternalItemInfo &o): ExternalItemInfoBase(o), current(o.current) { } - std::shared_ptr<ExternalItemInfo> makeCopy(DomItem &self) const + std::shared_ptr<ExternalItemInfo> makeCopy(const DomItem &self) const { return std::static_pointer_cast<ExternalItemInfo>(doCopy(self)); } @@ -475,7 +563,7 @@ public: std::shared_ptr<ExternalOwningItem> currentItem() const override { return current; } - DomItem currentItem(DomItem &self) const override { return self.copy(current); } + DomItem currentItem(const DomItem &self) const override { return self.copy(current); } std::shared_ptr<T> current; }; @@ -499,7 +587,7 @@ class QMLDOM_EXPORT LoadInfo final : public OwningItem Q_DECLARE_TR_FUNCTIONS(LoadInfo); protected: - std::shared_ptr<OwningItem> doCopy(DomItem &self) const override; + std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override; public: constexpr static DomType kindValue = DomType::LoadInfo; @@ -513,9 +601,9 @@ public: Done // fully loaded }; - LoadInfo(Path elPath = Path(), Status status = Status::NotStarted, int nLoaded = 0, + LoadInfo(const Path &elPath = Path(), Status status = Status::NotStarted, int nLoaded = 0, int derivedFrom = 0, - QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) + const QDateTime &lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) : OwningItem(derivedFrom, lastDataUpdateAt), m_elementCanonicalPath(elPath), m_status(status), @@ -534,23 +622,23 @@ public: } } - Path canonicalPath(DomItem &self) const override; + Path canonicalPath(const DomItem &self) const override; - bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; - std::shared_ptr<LoadInfo> makeCopy(DomItem &self) const + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override; + std::shared_ptr<LoadInfo> makeCopy(const DomItem &self) const { return std::static_pointer_cast<LoadInfo>(doCopy(self)); } - void addError(DomItem &self, ErrorMessage msg) override + void addError(const DomItem &self, ErrorMessage &&msg) override { - self.path(elementCanonicalPath()).addError(msg); + self.path(elementCanonicalPath()).addError(std::move(msg)); } - void addEndCallback(DomItem &self, std::function<void(Path, DomItem &, DomItem &)> callback); + void addEndCallback(const DomItem &self, std::function<void(Path, const DomItem &, const DomItem &)> callback); - void advanceLoad(DomItem &self); - void finishedLoadingDep(DomItem &self, const Dependency &d); - void execEnd(DomItem &self); + void advanceLoad(const DomItem &self); + void finishedLoadingDep(const DomItem &self, const Dependency &d); + void execEnd(const DomItem &self); Status status() const { @@ -595,15 +683,15 @@ public: } private: - void doAddDependencies(DomItem &self); - void addDependency(DomItem &self, const Dependency &dep); + void doAddDependencies(const DomItem &self); + void addDependency(const DomItem &self, const Dependency &dep); Path m_elementCanonicalPath; Status m_status; int m_nLoaded; QQueue<Dependency> m_toDo; QList<Dependency> m_inProgress; - QList<std::function<void(Path, DomItem &, DomItem &)>> m_endCallbacks; + QList<std::function<void(Path, const DomItem &, const DomItem &)>> m_endCallbacks; }; enum class EnvLookup { Normal, NoBase, BaseOnly }; @@ -617,21 +705,31 @@ public: enum class Cached { None, First, All }; Q_ENUM(Cached) - static RefCacheEntry forPath(DomItem &el, Path canonicalPath); - static bool addForPath(DomItem &el, Path canonicalPath, const RefCacheEntry &entry, + static RefCacheEntry forPath(const DomItem &el, const Path &canonicalPath); + static bool addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry, AddOption addOption = AddOption::KeepExisting); Cached cached = Cached::None; QList<Path> canonicalPaths; }; -class QMLDOM_EXPORT DomEnvironment final : public DomTop +class QMLDOM_EXPORT DomEnvironment final : public DomTop, + public std::enable_shared_from_this<DomEnvironment> { Q_GADGET Q_DECLARE_TR_FUNCTIONS(DomEnvironment); protected: - std::shared_ptr<OwningItem> doCopy(DomItem &self) const override; + std::shared_ptr<OwningItem> doCopy(const DomItem &self) const override; +private: + struct TypeReader + { + std::weak_ptr<DomEnvironment> m_env; + + QList<QQmlJS::DiagnosticMessage> + operator()(QQmlJSImporter *importer, const QString &filePath, + const QSharedPointer<QQmlJSScope> &scopeToPopulate); + }; public: enum class Option { Default = 0x0, @@ -651,121 +749,336 @@ public: Path canonicalPath() const override; using DomTop::canonicalPath; - bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override; - DomItem field(DomItem &self, QStringView name) const final override; + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override; + DomItem field(const DomItem &self, QStringView name) const final override; - std::shared_ptr<DomEnvironment> makeCopy(DomItem &self) const; + std::shared_ptr<DomEnvironment> makeCopy(const DomItem &self) const; - void loadFile(DomItem &self, QString filePath, QString logicalPath, Callback loadCallback, - Callback directDepsCallback, Callback endCallback, LoadOptions loadOptions, + void loadFile(const FileToLoad &file, const Callback &callback, std::optional<DomType> fileType = std::optional<DomType>(), - ErrorHandler h = nullptr); - void loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath, QString code, - QDateTime codeDate, Callback loadCallback, Callback directDepsCallback, - Callback endCallback, LoadOptions loadOptions, - std::optional<DomType> fileType = std::optional<DomType>(), - ErrorHandler h = nullptr); - void loadModuleDependency(DomItem &self, QString uri, Version v, - Callback loadCallback = nullptr, Callback endCallback = nullptr, - ErrorHandler = nullptr); - void loadBuiltins(DomItem &self, Callback callback = nullptr, ErrorHandler h = nullptr); + const ErrorHandler &h = nullptr /* used only in loadPendingDependencies*/); + void loadBuiltins(const Callback &callback = nullptr, const ErrorHandler &h = nullptr); + void loadModuleDependency(const QString &uri, Version v, const Callback &callback = nullptr, + const ErrorHandler & = nullptr); + void removePath(const QString &path); std::shared_ptr<DomUniverse> universe() const; - QSet<QString> moduleIndexUris(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; - QSet<int> moduleIndexMajorVersions(DomItem &self, QString uri, + QSet<QString> moduleIndexUris(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; + QSet<int> moduleIndexMajorVersions(const DomItem &self, const QString &uri, EnvLookup lookup = EnvLookup::Normal) const; - std::shared_ptr<ModuleIndex> moduleIndexWithUri(DomItem &self, QString uri, int majorVersion, + std::shared_ptr<ModuleIndex> moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion, EnvLookup lookup, Changeable changeable, - ErrorHandler errorHandler = nullptr); - std::shared_ptr<ModuleIndex> moduleIndexWithUri(DomItem &self, QString uri, int majorVersion, + const ErrorHandler &errorHandler = nullptr); + std::shared_ptr<ModuleIndex> moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion, EnvLookup lookup = EnvLookup::Normal) const; std::shared_ptr<ExternalItemInfo<QmlDirectory>> - qmlDirectoryWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const; - QSet<QString> qmlDirectoryPaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const; + qmlDirectoryWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const; + QSet<QString> qmlDirectoryPaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const; std::shared_ptr<ExternalItemInfo<QmldirFile>> - qmldirFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const; - QSet<QString> qmldirFilePaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const; + qmldirFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const; + QSet<QString> qmldirFilePaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const; std::shared_ptr<ExternalItemInfoBase> - qmlDirWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const; - QSet<QString> qmlDirPaths(DomItem &self, EnvLookup options = EnvLookup::Normal) const; + qmlDirWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const; + QSet<QString> qmlDirPaths(const DomItem &self, EnvLookup options = EnvLookup::Normal) const; std::shared_ptr<ExternalItemInfo<QmlFile>> - qmlFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const; - QSet<QString> qmlFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; + qmlFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const; + QSet<QString> qmlFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; std::shared_ptr<ExternalItemInfo<JsFile>> - jsFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const; - QSet<QString> jsFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; + jsFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const; + QSet<QString> jsFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; std::shared_ptr<ExternalItemInfo<QmltypesFile>> - qmltypesFileWithPath(DomItem &self, QString path, EnvLookup options = EnvLookup::Normal) const; - QSet<QString> qmltypesFilePaths(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; + qmltypesFileWithPath(const DomItem &self, const QString &path, EnvLookup options = EnvLookup::Normal) const; + QSet<QString> qmltypesFilePaths(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; std::shared_ptr<ExternalItemInfo<GlobalScope>> - globalScopeWithName(DomItem &self, QString name, EnvLookup lookup = EnvLookup::Normal) const; + globalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup = EnvLookup::Normal) const; std::shared_ptr<ExternalItemInfo<GlobalScope>> - ensureGlobalScopeWithName(DomItem &self, QString name, EnvLookup lookup = EnvLookup::Normal); - QSet<QString> globalScopeNames(DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; - - explicit DomEnvironment(QStringList loadPaths, Options options = Option::SingleThreaded, - std::shared_ptr<DomUniverse> universe = nullptr); - explicit DomEnvironment(std::shared_ptr<DomEnvironment> parent, QStringList loadPaths, - Options options = Option::SingleThreaded); + ensureGlobalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup = EnvLookup::Normal); + QSet<QString> globalScopeNames(const DomItem &self, EnvLookup lookup = EnvLookup::Normal) const; + + explicit DomEnvironment(const QStringList &loadPaths, Options options = Option::SingleThreaded, + DomCreationOptions domCreationOptions = None, + const std::shared_ptr<DomUniverse> &universe = nullptr); + explicit DomEnvironment(const std::shared_ptr<DomEnvironment> &parent, + const QStringList &loadPaths, Options options = Option::SingleThreaded, + DomCreationOptions domCreationOptions = None); DomEnvironment(const DomEnvironment &o) = delete; - static DomItem create(QStringList loadPaths, Options options = Option::SingleThreaded, - DomItem &universe = DomItem::empty); - - std::shared_ptr<ExternalItemInfo<QmlFile>> - addQmlFile(std::shared_ptr<QmlFile> file, AddOption option = AddOption::KeepExisting); - std::shared_ptr<ExternalItemInfo<QmlDirectory>> - addQmlDirectory(std::shared_ptr<QmlDirectory> file, AddOption option = AddOption::KeepExisting); - std::shared_ptr<ExternalItemInfo<QmldirFile>> - addQmldirFile(std::shared_ptr<QmldirFile> file, AddOption option = AddOption::KeepExisting); - std::shared_ptr<ExternalItemInfo<QmltypesFile>> - addQmltypesFile(std::shared_ptr<QmltypesFile> file, AddOption option = AddOption::KeepExisting); - std::shared_ptr<ExternalItemInfo<JsFile>> addJsFile(std::shared_ptr<JsFile> file, - AddOption option = AddOption::KeepExisting); - std::shared_ptr<ExternalItemInfo<GlobalScope>> - addGlobalScope(std::shared_ptr<GlobalScope> file, AddOption option = AddOption::KeepExisting); - - bool commitToBase(DomItem &self, std::shared_ptr<DomEnvironment> validEnv = nullptr); - - void addLoadInfo(DomItem &self, std::shared_ptr<LoadInfo> loadInfo); - std::shared_ptr<LoadInfo> loadInfo(Path path) const; + static std::shared_ptr<DomEnvironment> + create(const QStringList &loadPaths, Options options = Option::SingleThreaded, + DomCreationOptions creationOptions = DomCreationOption::None, + const DomItem &universe = DomItem::empty); + + // TODO AddOption can easily be removed later. KeepExisting option only used in one + // place which will be removed in https://codereview.qt-project.org/c/qt/qtdeclarative/+/523217 + void addQmlFile(const std::shared_ptr<QmlFile> &file, + AddOption option = AddOption::KeepExisting); + void addQmlDirectory(const std::shared_ptr<QmlDirectory> &file, + AddOption option = AddOption::KeepExisting); + void addQmldirFile(const std::shared_ptr<QmldirFile> &file, + AddOption option = AddOption::KeepExisting); + void addQmltypesFile(const std::shared_ptr<QmltypesFile> &file, + AddOption option = AddOption::KeepExisting); + void addJsFile(const std::shared_ptr<JsFile> &file, AddOption option = AddOption::KeepExisting); + void addGlobalScope(const std::shared_ptr<GlobalScope> &file, + AddOption option = AddOption::KeepExisting); + + bool commitToBase( + const DomItem &self, const std::shared_ptr<DomEnvironment> &validEnv = nullptr); + + void addDependenciesToLoad(const Path &path); + void addLoadInfo( + const DomItem &self, const std::shared_ptr<LoadInfo> &loadInfo); + std::shared_ptr<LoadInfo> loadInfo(const Path &path) const; QList<Path> loadInfoPaths() const; QHash<Path, std::shared_ptr<LoadInfo>> loadInfos() const; - void loadPendingDependencies(DomItem &self); - bool finishLoadingDependencies(DomItem &self, int waitMSec = 30000); - void addWorkForLoadInfo(Path elementCanonicalPath); + void loadPendingDependencies(); + bool finishLoadingDependencies(int waitMSec = 30000); + void addWorkForLoadInfo(const Path &elementCanonicalPath); Options options() const; std::shared_ptr<DomEnvironment> base() const; QStringList loadPaths() const; + QStringList qmldirFiles() const; QString globalScopeName() const; static QList<Import> defaultImplicitImports(); QList<Import> implicitImports() const; - void addAllLoadedCallback(DomItem &self, Callback c); + void addAllLoadedCallback(const DomItem &self, Callback c); void clearReferenceCache(); void setLoadPaths(const QStringList &v); + // Helper structure reflecting the change in the map once loading / fetching is completed + // formerItem - DomItem representing value (ExternalItemInfo) existing in the map before the + // loading && parsing. Might be empty (if didn't exist / failure) or equal to currentItem + // currentItem - DomItem representing current map value + struct LoadResult + { + DomItem formerItem; + DomItem currentItem; + }; + // TODO(QTBUG-121171) + template <typename T> + LoadResult insertOrUpdateExternalItemInfo(const QString &path, std::shared_ptr<T> extItem) + { + // maybe in the next revision this all can be just substituted by the addExternalItem + DomItem env(shared_from_this()); + // try to fetch from the current env. + if (auto curValue = lookup<T>(path, EnvLookup::NoBase)) { + // found in the "initial" env + return { env.copy(curValue), env.copy(curValue) }; + } + std::shared_ptr<ExternalItemInfo<T>> newCurValue; + // try to fetch from the base env + auto valueInBase = lookup<T>(path, EnvLookup::BaseOnly); + if (!valueInBase) { + // Nothing found. Just create an externalItemInfo which will be inserted + newCurValue = std::make_shared<ExternalItemInfo<T>>(std::move(extItem), + QDateTime::currentDateTimeUtc()); + } else { + // prepare updated value as a copy of the value from the Base to be inserted + newCurValue = valueInBase->makeCopy(env); + if (newCurValue->current != extItem) { + newCurValue->current = std::move(extItem); + newCurValue->setCurrentExposedAt(QDateTime::currentDateTimeUtc()); + } + } + // Before inserting new or updated value, check one more time, if ItemInfo is already + // present + // lookup<> can't be used here because of the data-race + { + QMutexLocker l(mutex()); + auto &map = getMutableRefToMap<T>(); + const auto &it = map.find(path); + if (it != map.end()) + return { env.copy(*it), env.copy(*it) }; + // otherwise insert + map.insert(path, newCurValue); + } + return { env.copy(valueInBase), env.copy(newCurValue) }; + } + + template <typename T> + void addExternalItemInfo(const DomItem &newExtItem, const Callback &loadCallback, + const Callback &endCallback) + { + // get either Valid "file" from the ExternalItemPair or the current (wip) "file" + std::shared_ptr<T> newItemPtr; + if (options() & DomEnvironment::Option::KeepValid) + newItemPtr = newExtItem.field(Fields::validItem).ownerAs<T>(); + if (!newItemPtr) + newItemPtr = newExtItem.field(Fields::currentItem).ownerAs<T>(); + Q_ASSERT(newItemPtr && "envCallbackForFile reached without current file"); + + auto loadResult = insertOrUpdateExternalItemInfo(newExtItem.canonicalFilePath(), + std::move(newItemPtr)); + Path p = loadResult.currentItem.canonicalPath(); + { + auto depLoad = qScopeGuard([p, this, endCallback] { + addDependenciesToLoad(p); + // add EndCallback to the queue, which should be called once all dependencies are + // loaded + if (endCallback) { + DomItem env = DomItem(shared_from_this()); + addAllLoadedCallback( + env, [p, endCallback](Path, const DomItem &, const DomItem &env) { + DomItem el = env.path(p); + endCallback(p, el, el); + }); + } + }); + // call loadCallback + if (loadCallback) { + loadCallback(p, loadResult.formerItem, loadResult.currentItem); + } + } + } + void populateFromQmlFile(MutableDomItem &&qmlFile); + DomCreationOptions domCreationOptions() const { return m_domCreationOptions; } + private: friend class RefCacheEntry; - template<typename T> + + void loadFile(const FileToLoad &file, const Callback &loadCallback, const Callback &endCallback, + std::optional<DomType> fileType = std::optional<DomType>(), + const ErrorHandler &h = nullptr); + + void loadModuleDependency(const DomItem &self, const QString &uri, Version v, + Callback loadCallback = nullptr, Callback endCallback = nullptr, + const ErrorHandler & = nullptr); + + template <typename T> QSet<QString> getStrings(function_ref<QSet<QString>()> getBase, const QMap<QString, T> &selfMap, EnvLookup lookup) const; - Callback callbackForQmlDirectory(DomItem &self, Callback loadCallback, - Callback directDepsCallback, Callback endCallback); - Callback callbackForQmlFile(DomItem &self, Callback loadCallback, Callback directDepsCallback, - Callback endCallback); - Callback callbackForQmltypesFile(DomItem &self, Callback loadCallback, - Callback directDepsCallback, Callback endCallback); - Callback callbackForQmldirFile(DomItem &self, Callback loadCallback, - Callback directDepsCallback, Callback endCallback); + template <typename T> + const QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &getConstRefToMap() const + { + Q_ASSERT(!mutex()->tryLock()); + if constexpr (std::is_same_v<T, GlobalScope>) { + return m_globalScopeWithName; + } + if constexpr (std::is_same_v<T, QmlDirectory>) { + return m_qmlDirectoryWithPath; + } + if constexpr (std::is_same_v<T, QmldirFile>) { + return m_qmldirFileWithPath; + } + if constexpr (std::is_same_v<T, QmlFile>) { + return m_qmlFileWithPath; + } + if constexpr (std::is_same_v<T, JsFile>) { + return m_jsFileWithPath; + } + if constexpr (std::is_same_v<T, QmltypesFile>) { + return m_qmltypesFileWithPath; + } + Q_UNREACHABLE(); + } + + template <typename T> + std::shared_ptr<ExternalItemInfo<T>> lookup(const QString &path, EnvLookup options) const + { + if (options != EnvLookup::BaseOnly) { + QMutexLocker l(mutex()); + const auto &map = getConstRefToMap<T>(); + const auto &it = map.find(path); + if (it != map.end()) + return *it; + } + if (options != EnvLookup::NoBase && m_base) + return m_base->lookup<T>(path, options); + return {}; + } + + template <typename T> + QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &getMutableRefToMap() + { + Q_ASSERT(!mutex()->tryLock()); + if constexpr (std::is_same_v<T, QmlDirectory>) { + return m_qmlDirectoryWithPath; + } + if constexpr (std::is_same_v<T, QmldirFile>) { + return m_qmldirFileWithPath; + } + if constexpr (std::is_same_v<T, QmlFile>) { + return m_qmlFileWithPath; + } + if constexpr (std::is_same_v<T, JsFile>) { + return m_jsFileWithPath; + } + if constexpr (std::is_same_v<T, QmltypesFile>) { + return m_qmltypesFileWithPath; + } + if constexpr (std::is_same_v<T, GlobalScope>) { + return m_globalScopeWithName; + } + Q_UNREACHABLE(); + } + + template <typename T> + void addExternalItem(std::shared_ptr<T> file, QString key, AddOption option) + { + if (!file) + return; + + auto eInfo = std::make_shared<ExternalItemInfo<T>>(file, QDateTime::currentDateTimeUtc()); + // Lookup helper can't be used here, because it introduces data-race otherwise + // (other modifications might happen between the lookup and the insert) + QMutexLocker l(mutex()); + auto &map = getMutableRefToMap<T>(); + const auto &it = map.find(key); + if (it != map.end() && option == AddOption::KeepExisting) + return; + map.insert(key, eInfo); + } + + using FetchResult = + QPair<std::shared_ptr<ExternalItemInfoBase>, std::shared_ptr<ExternalItemInfoBase>>; + // This function tries to get an Info object about the ExternalItem from the current env + // and depending on the result and options tries to fetch it from the Parent env, + // saving a copy with an updated timestamp + template <typename T> + FetchResult fetchFileFromEnvs(const FileToLoad &file) + { + const auto &path = file.canonicalPath(); + // lookup only in the current env + if (auto value = lookup<T>(path, EnvLookup::NoBase)) { + return qMakePair(value, value); + } + // try to find the file in the base(parent) Env and insert if found + if (options() & Option::NoReload) { + if (auto baseV = lookup<T>(path, EnvLookup::BaseOnly)) { + // Watch out! QTBUG-121171 + // It's possible between the lookup and creation of curVal, baseV && baseV->current + // might have changed + // Prepare a value to be inserted as copy of the value from Base + auto curV = std::make_shared<ExternalItemInfo<T>>( + baseV->current, QDateTime::currentDateTimeUtc(), baseV->revision(), + baseV->lastDataUpdateAt()); + // Lookup one more time if the value was already inserted to the current env + // Lookup can't be used here because of the data-race + { + QMutexLocker l(mutex()); + auto &map = getMutableRefToMap<T>(); + const auto &it = map.find(path); + if (it != map.end()) + return qMakePair(*it, *it); + // otherwise insert + map.insert(path, curV); + } + return qMakePair(baseV, curV); + } + } + return qMakePair(nullptr, nullptr); + } + + Callback getLoadCallbackFor(DomType fileType, const Callback &loadCallback); std::shared_ptr<ModuleIndex> lookupModuleInEnv(const QString &uri, int majorVersion) const; // ModuleLookupResult contains the ModuleIndex pointer, and an indicator whether it was found @@ -776,11 +1089,12 @@ private: Origin fromBase = FromGlobal; }; // helper function used by the moduleIndexWithUri methods - ModuleLookupResult moduleIndexWithUriHelper(DomItem &self, QString uri, int majorVersion, + ModuleLookupResult moduleIndexWithUriHelper(const DomItem &self, const QString &uri, int majorVersion, EnvLookup lookup = EnvLookup::Normal) const; const Options m_options; const std::shared_ptr<DomEnvironment> m_base; + std::shared_ptr<DomEnvironment> m_lastValidBase; const std::shared_ptr<DomUniverse> m_universe; QStringList m_loadPaths; // paths for qml QString m_globalScopeName; @@ -797,6 +1111,18 @@ private: QList<Import> m_implicitImports; QList<Callback> m_allLoadedCallback; QHash<Path, RefCacheEntry> m_referenceCache; + DomCreationOptions m_domCreationOptions; + + struct SemanticAnalysis + { + SemanticAnalysis(const QStringList &loadPaths); + void setLoadPaths(const QStringList &loadPaths); + + std::shared_ptr<QQmlJSResourceFileMapper> m_mapper; + std::shared_ptr<QQmlJSImporter> m_importer; + }; + std::optional<SemanticAnalysis> m_semanticAnalysis; + SemanticAnalysis &semanticAnalysis(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(DomEnvironment::Options) |