diff options
Diffstat (limited to 'src/qmldom/qqmldomitem.cpp')
-rw-r--r-- | src/qmldom/qqmldomitem.cpp | 1903 |
1 files changed, 1030 insertions, 873 deletions
diff --git a/src/qmldom/qqmldomitem.cpp b/src/qmldom/qqmldomitem.cpp index e3f871acf0..b885422c1d 100644 --- a/src/qmldom/qqmldomitem.cpp +++ b/src/qmldom/qqmldomitem.cpp @@ -1,5 +1,9 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "qqmldomattachedinfo_p.h" +#include "qqmldomconstants_p.h" +#include "qqmldomitem_p.h" +#include "qqmldompath_p.h" #include "qqmldomtop_p.h" #include "qqmldomelements_p.h" #include "qqmldomexternalitems_p.h" @@ -11,6 +15,8 @@ #include "qqmldomcompare_p.h" #include "qqmldomastdumper_p.h" #include "qqmldomlinewriter_p.h" +#include "qqmldom_utils_p.h" +#include "qqmldomscriptelements_p.h" #include <QtQml/private/qqmljslexer_p.h> #include <QtQml/private/qqmljsparser_p.h> @@ -32,6 +38,8 @@ #include <QtCore/QScopeGuard> #include <QtCore/QtGlobal> #include <QtCore/QTimeZone> +#include <optional> +#include <type_traits> QT_BEGIN_NAMESPACE @@ -41,6 +49,37 @@ namespace Dom { Q_LOGGING_CATEGORY(writeOutLog, "qt.qmldom.writeOut", QtWarningMsg); static Q_LOGGING_CATEGORY(refLog, "qt.qmldom.ref", QtWarningMsg); +template<class... TypeList> +struct CheckDomElementT; + +template<class... Ts> +struct CheckDomElementT<std::variant<Ts...>> : std::conjunction<IsInlineDom<Ts>...> +{ +}; + +/*! + \internal + \class QQmljs::Dom::ElementT + + \brief A variant that contains all the Dom elements that an DomItem can contain. + + Types in this variant are divided in two categories: normal Dom elements and internal Dom + elements. + The first ones are inheriting directly or indirectly from DomBase, and are the usual elements + that a DomItem can wrap around, like a QmlFile or an QmlObject. They should all appear in + ElementT as pointers, e.g. QmlFile*. + The internal Dom elements are a little bit special. They appear in ElementT without pointer, do + not inherit from DomBase \b{but} should behave like a smart DomBase-pointer. That is, they should + dereference as if they were a DomBase* pointing to a normal DomElement by implementing + operator->() and operator*(). + Adding types here that are neither inheriting from DomBase nor implementing a smartpointer to + DomBase will throw compilation errors in the std::visit()-calls on this type. +*/ +static_assert(CheckDomElementT<ElementT>::value, + "Types in ElementT must either be a pointer to a class inheriting " + "from DomBase or (for internal Dom structures) implement a smart " + "pointer pointing to a class inheriting from DomBase"); + using std::shared_ptr; /*! \internal @@ -59,17 +98,36 @@ The subclass *must* have a \endcode entry with its kind to enable casting usng the DomItem::as DomItem::ownerAs templates. -The minimal overload set to be usable is: +The minimal overload set to be usable consists of following methods: +\list +\li \c{kind()} returns the kind of the current element: +\code + Kind kind() const override { return kindValue; } +\endcode + +\li \c{pathFromOwner()} returns the path from the owner to the current element \code - Kind kind() const override { return kindValue; } // returns the kind of the current element - Path pathFromOwner(DomItem &self) const override; // returns the path from the owner to the -current element Path canonicalPath(DomItem &self) const override; // returns the path from virtual -bool iterateDirectSubpaths(DomItem &self, function_ref<bool(Path, DomItem)>) const = 0; // iterates -the *direct* subpaths, returns false if a quick end was requested \endcode But you probably want to -subclass either DomElement of OwningItem for your element. DomElement stores its pathFromOwner, and -computes the canonicalPath from it and its owner. OwningItem is the unit for updates to the Dom -model, exposed changes always change at least one OwningItem. They have their lifetime handled with -shared_ptr and own (i.e. are responsible of freeing) other items in them. + Path pathFromOwner(const DomItem &self) const override; +\endcode + +\li \c{canonicalPath()} returns the path +\code + Path canonicalPath(const DomItem &self) const override; +\endcode + +\li \c{iterateDirectSubpaths} iterates the *direct* subpaths/children and returns false if a quick +end was requested: +\code +bool iterateDirectSubpaths(const DomItem &self, function_ref<bool(Path, DomItem)>) const = 0; +\endcode + +\endlist + +But you probably want to subclass either \c DomElement or \c OwningItem for your element. \c +DomElement stores its \c pathFromOwner, and computes the \c canonicalPath from it and its owner. \c +OwningItem is the unit for updates to the Dom model, exposed changes always change at least one \c +OwningItem. They have their lifetime handled with \c shared_ptr and own (i.e. are responsible of +freeing) other items in them. \sa QQml::Dom::DomItem, QQml::Dom::DomElement, QQml::Dom::OwningItem */ @@ -170,20 +228,7 @@ bool domTypeIsScope(DomType k) } } -QCborValue locationToData(SourceLocation loc, QStringView strValue) -{ - QCborMap res({ - {QStringLiteral(u"offset"), loc.offset}, - {QStringLiteral(u"length"), loc.length}, - {QStringLiteral(u"startLine"), loc.startLine}, - {QStringLiteral(u"startColumn"), loc.startColumn} - }); - if (!strValue.isEmpty()) - res.insert(QStringLiteral(u"strValue"), QCborValue(strValue)); - return res; -} - -QString DomBase::canonicalFilePath(DomItem &self) const +QString DomBase::canonicalFilePath(const DomItem &self) const { auto parent = containingObject(self); if (parent) @@ -191,21 +236,21 @@ QString DomBase::canonicalFilePath(DomItem &self) const return QString(); } -void DomBase::writeOut(DomItem &self, OutWriter &) const +void DomBase::writeOut(const DomItem &self, OutWriter &) const { qCWarning(writeOutLog) << "Ignoring unsupported writeOut for " << domTypeToString(kind()) << ":" << self.canonicalPath(); } -ConstantData::ConstantData(Path pathFromOwner, QCborValue value, Options options) +ConstantData::ConstantData(const Path &pathFromOwner, const QCborValue &value, Options options) : DomElement(pathFromOwner), m_value(value), m_options(options) {} -bool ConstantData::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) +bool ConstantData::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const { static QHash<QString, QString> knownFields; static QBasicMutex m; - auto toField = [](QString f) -> QStringView { + auto toField = [](const QString &f) -> QStringView { QMutexLocker l(&m); if (!knownFields.contains(f)) knownFields[f] = f; @@ -302,6 +347,41 @@ It does not keep any pointers to internal elements, but rather the path to them, it every time it needs. */ +FileToLoad::FileToLoad(const std::weak_ptr<DomEnvironment> &environment, + const QString &canonicalPath, const QString &logicalPath, + const std::optional<InMemoryContents> &content) + : m_environment(environment), + m_canonicalPath(canonicalPath), + m_logicalPath(logicalPath), + m_content(content) +{ +} + +FileToLoad FileToLoad::fromMemory(const std::weak_ptr<DomEnvironment> &environment, + const QString &path, const QString &code) +{ + const QString canonicalPath = QFileInfo(path).canonicalFilePath(); + return { + environment, + canonicalPath, + path, + InMemoryContents{ code }, + }; +} + +FileToLoad FileToLoad::fromFileSystem(const std::weak_ptr<DomEnvironment> &environment, + const QString &path) +{ + // make the path canonical so the file content can be loaded from it later + const QString canonicalPath = QFileInfo(path).canonicalFilePath(); + return { + environment, + canonicalPath, + path, + std::nullopt, + }; +} + ErrorGroup DomItem::domErrorGroup = NewErrorGroup("Dom"); DomItem DomItem::empty = DomItem(); @@ -317,7 +397,7 @@ ErrorGroups DomItem::myResolveErrors() return res; } -Path DomItem::canonicalPath() +Path DomItem::canonicalPath() const { Path res = visitEl([this](auto &&el) { return el->canonicalPath(*this); }); if (!(!res || res.headKind() == Path::Kind::Root)) { @@ -328,14 +408,20 @@ Path DomItem::canonicalPath() } -DomItem DomItem::containingObject() +DomItem DomItem::containingObject() const { return visitEl([this](auto &&el) { return el->containingObject(*this); }); } -DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions) +/*! + \internal + \brief Returns the QmlObject that this belongs to. + + qmlObject() might also return the object of a component if GoTo:MostLikely is used. + */ +DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions) const { - if (DomItem res = filterUp([](DomType k, DomItem &) { return k == DomType::QmlObject; }, + if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlObject; }, filterOptions)) return res; if (options == GoTo::MostLikely) { @@ -345,7 +431,7 @@ DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions) return DomItem(); } -DomItem DomItem::fileObject(GoTo options) +DomItem DomItem::fileObject(GoTo options) const { DomItem res = *this; DomType k = res.internalKind(); @@ -368,19 +454,12 @@ DomItem DomItem::fileObject(GoTo options) return res; } -DomItem DomItem::rootQmlObject(GoTo options) +DomItem DomItem::rootQmlObject(GoTo options) const { - if (DomItem res = filterUp([](DomType k, DomItem &) { return k == DomType::QmlObject; }, - FilterUpOptions::ReturnInner)) - return res; - if (options == GoTo::MostLikely) { - if (DomItem comp = component(options)) - return comp.field(Fields::objects).index(0); - } - return DomItem(); + return qmlObject(options, FilterUpOptions::ReturnInner); } -DomItem DomItem::container() +DomItem DomItem::container() const { Path path = pathFromOwner(); if (!path) @@ -391,7 +470,7 @@ DomItem DomItem::container() return containingObject(); } -DomItem DomItem::globalScope() +DomItem DomItem::globalScope() const { if (internalKind() == DomType::GlobalScope) return *this; @@ -403,23 +482,35 @@ DomItem DomItem::globalScope() return DomItem(); } -DomItem DomItem::owner() +/*! + \internal + \brief The owner of an element, for an qmlObject this is the containing qml file. + */ +DomItem DomItem::owner() const { if (domTypeIsOwningItem(m_kind) || m_kind == DomType::Empty) return *this; - return std::visit( - [this](auto &&el) { return DomItem(this->m_top, el, this->m_ownerPath, el.get()); }, - *m_owner); + return std::visit([this](auto &&el) { + if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) + return DomItem(); + else + return DomItem(this->m_top, el, this->m_ownerPath, el.get()); + }, m_owner); } -DomItem DomItem::top() +DomItem DomItem::top() const { if (domTypeIsTopItem(m_kind) || m_kind == DomType::Empty) return *this; - return std::visit([](auto &&el) { return DomItem(el, el, Path(), el.get()); }, *m_top); + return std::visit([](auto &&el) { + if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) + return DomItem(); + else + return DomItem(el, el, Path(), el.get()); + }, m_top); } -DomItem DomItem::environment() +DomItem DomItem::environment() const { DomItem res = top(); if (res.internalKind() == DomType::DomEnvironment) @@ -427,7 +518,7 @@ DomItem DomItem::environment() return DomItem(); // we are in the universe, and cannot go back to the environment... } -DomItem DomItem::universe() +DomItem DomItem::universe() const { DomItem res = top(); if (res.internalKind() == DomType::DomUniverse) @@ -437,91 +528,169 @@ DomItem DomItem::universe() return DomItem(); // we should be in an empty DomItem already... } -DomItem DomItem::filterUp(function_ref<bool(DomType k, DomItem &)> filter, FilterUpOptions options) +/*! + \internal + Shorthand to obtain the ScriptExpression DomItem, in which this DomItem is defined. + Returns an empty DomItem if the item is not defined inside a ScriptExpression. + \sa goToFile() + */ +DomItem DomItem::containingScriptExpression() const +{ + if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::ScriptExpression; }, + FilterUpOptions::ReturnOuter)) + return res; + return DomItem(); +} + +/*! + \internal + Shorthand to obtain the QmlFile DomItem, in which this DomItem is defined. + Returns an empty DomItem if the item is not defined in a QML file. + \sa goToFile() + */ +DomItem DomItem::containingFile() const +{ + if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlFile; }, + FilterUpOptions::ReturnOuter)) + return res; + return DomItem(); +} + +/*! + \internal + Shorthand to obtain the QmlFile DomItem from a canonicalPath. + \sa containingFile() + */ +DomItem DomItem::goToFile(const QString &canonicalPath) const +{ + Q_UNUSED(canonicalPath); + DomItem file = + top().field(Fields::qmlFileWithPath).key(canonicalPath).field(Fields::currentItem); + return file; +} + +/*! + \internal + In the DomItem hierarchy, go \c n levels up. + */ +DomItem DomItem::goUp(int n) const +{ + Path path = canonicalPath(); + // first entry of path is usually top(), and you cannot go up from top(). + if (path.length() < n + 1) + return DomItem(); + + DomItem parent = top().path(path.dropTail(n)); + return parent; +} + +/*! + \internal + In the DomItem hierarchy, go 1 level up to get the direct parent. + */ +DomItem DomItem::directParent() const { - DomItem it = *this; - DomType k = it.internalKind(); + return goUp(1); +} + +/*! +\internal +Finds the first element in the DomItem hierarchy that satisfies filter. +Use options to set the search direction, see also \l{FilterUpOptions}. +*/ +DomItem DomItem::filterUp(function_ref<bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const +{ + if (options == FilterUpOptions::ReturnOuter && filter(internalKind(), *this)) { + return *this; + } + switch (options) { case FilterUpOptions::ReturnOuter: case FilterUpOptions::ReturnOuterNoSelf: { - bool checkTop = (options == FilterUpOptions::ReturnOuter); - while (k != DomType::Empty) { - if (checkTop && filter(k, it)) - return it; - checkTop = true; - if (!domTypeIsOwningItem(k)) { - DomItem el = it.owner(); - DomItem res; - k = DomType::Empty; - Path pp = it.pathFromOwner(); - DomType k2 = el.internalKind(); - if (filter(k2, el)) { - k = k2; - res = el; - } - for (Path p : pp.mid(0, pp.length() - 1)) { - el = el.path(p); - DomType k2 = el.internalKind(); - if (filter(k2, el)) { - k = k2; - res = el; - } - } - if (k != DomType::Empty) - return res; - it = it.owner(); + for (DomItem current = *this, previous = DomItem(); current; + previous = current, current = current.directParent()) { + if (filter(current.internalKind(), current)) { + if (options != FilterUpOptions::ReturnOuterNoSelf || current != *this) + return current; } - it = it.containingObject(); - k = it.internalKind(); } - } break; + break; + } case FilterUpOptions::ReturnInner: - while (k != DomType::Empty) { - if (!domTypeIsOwningItem(k)) { - DomItem el = owner(); - Path pp = pathFromOwner(); - for (Path p : pp) { - DomItem child = el.path(p); - DomType k2 = child.internalKind(); - if (filter(k2, child)) - return child; - el = child; - } - it = it.owner(); - } - it = it.containingObject(); - k = it.internalKind(); + DomItem current = top(); + for (const Path ¤tPath : canonicalPath()) { + current = current.path(currentPath); + if (filter(current.internalKind(), current)) + return current; } break; } + return DomItem(); } -DomItem DomItem::scope(FilterUpOptions options) +DomItem DomItem::scope(FilterUpOptions options) const { - DomItem res = filterUp([](DomType, DomItem &el) { return el.isScope(); }, options); + DomItem res = filterUp([](DomType, const DomItem &el) { return el.isScope(); }, options); return res; } -DomItem DomItem::get(ErrorHandler h, QList<Path> *visitedRefs) +QQmlJSScope::ConstPtr DomItem::nearestSemanticScope() const +{ + QQmlJSScope::ConstPtr scope; + visitUp([&scope](const DomItem &item) { + scope = item.semanticScope(); + return !scope; // stop when scope was true + }); + return scope; +} + +QQmlJSScope::ConstPtr DomItem::semanticScope() const +{ + QQmlJSScope::ConstPtr scope = std::visit( + [](auto &&e) -> QQmlJSScope::ConstPtr { + using T = std::remove_cv_t<std::remove_reference_t<decltype(e)>>; + if constexpr (std::is_same_v<T, const QmlObject *>) { + return e->semanticScope(); + } else if constexpr (std::is_same_v<T, const QmlComponent *>) { + return e->semanticScope(); + } else if constexpr (std::is_same_v<T, const QmltypesComponent *>) { + return e->semanticScope(); + } else if constexpr (std::is_same_v<T, SimpleObjectWrap>) { + if (const MethodInfo *mi = e->template as<MethodInfo>()) { + return mi->semanticScope(); + } + if (const auto *propertyDefinition = e->template as<PropertyDefinition>()) { + return propertyDefinition->semanticScope(); + } + } else if constexpr (std::is_same_v<T, ScriptElementDomWrapper>) { + return e.element().base()->semanticScope(); + } + return {}; + }, + m_element); + return scope; +} + +DomItem DomItem::get(const ErrorHandler &h, QList<Path> *visitedRefs) const { if (const Reference *refPtr = as<Reference>()) return refPtr->get(*this, h, visitedRefs); return DomItem(); } -QList<DomItem> DomItem::getAll(ErrorHandler h, QList<Path> *visitedRefs) +QList<DomItem> DomItem::getAll(const ErrorHandler &h, QList<Path> *visitedRefs) const { if (const Reference *refPtr = as<Reference>()) return refPtr->getAll(*this, h, visitedRefs); return {}; } -PropertyInfo DomItem::propertyInfoWithName(QString name) +PropertyInfo DomItem::propertyInfoWithName(const QString &name) const { PropertyInfo pInfo; - visitPrototypeChain([&pInfo, name](DomItem &obj) { - return obj.visitLocalSymbolsNamed(name, [&pInfo, name](DomItem &el) { + visitPrototypeChain([&pInfo, name](const DomItem &obj) { + return obj.visitLocalSymbolsNamed(name, [&pInfo, name](const DomItem &el) { switch (el.internalKind()) { case DomType::Binding: pInfo.bindings.append(el); @@ -538,10 +707,10 @@ PropertyInfo DomItem::propertyInfoWithName(QString name) return pInfo; } -QSet<QString> DomItem::propertyInfoNames() +QSet<QString> DomItem::propertyInfoNames() const { QSet<QString> res; - visitPrototypeChain([&res](DomItem &obj) { + visitPrototypeChain([&res](const DomItem &obj) { res += obj.propertyDefs().keys(); res += obj.bindings().keys(); return true; @@ -549,10 +718,10 @@ QSet<QString> DomItem::propertyInfoNames() return res; } -DomItem DomItem::component(GoTo options) +DomItem DomItem::component(GoTo options) const { if (DomItem res = filterUp( - [](DomType kind, DomItem &) { + [](DomType kind, const DomItem &) { return kind == DomType::QmlComponent || kind == DomType::QmltypesComponent || kind == DomType::GlobalComponent; }, @@ -597,8 +766,8 @@ static QMap<LookupType, QString> lookupTypeToStringMap() return map; } -bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHandler, - ResolveOptions options, Path fullPath, QList<Path> *visitedRefs) +bool DomItem::resolve(const Path &path, DomItem::Visitor visitor, const ErrorHandler &errorHandler, + ResolveOptions options, const Path &fullPath, QList<Path> *visitedRefs) const { QList<Path> vRefs; Path fPath = fullPath; @@ -635,13 +804,12 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan myResolveErrors().error(tr("Root context %1 is not known").arg(path.headName())).handle(errorHandler); return false; } - toDos[0] = {root, 1}; + toDos[0] = {std::move(root), 1}; } else { toDos[0] = {*this, 0}; } while (!toDos.isEmpty()) { - auto toDo = toDos.last(); - toDos.removeLast(); + const ResolveToDo toDo = toDos.takeLast(); { auto idNow = toDo.item.id(); if (idNow == quintptr(0) && toDo.item == *this) @@ -678,7 +846,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan } if (visitedRefs->contains(refRef)) { myResolveErrors() - .error([visitedRefs, refRef](Sink sink) { + .error([visitedRefs, refRef](const Sink &sink) { const QString msg = tr("Circular reference:") + QLatin1Char('\n'); sink(QStringView{msg}); for (const Path &vPath : *visitedRefs) { @@ -695,7 +863,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan DomItem resolveRes; it.resolve( toResolve, - [&resolveRes](Path, DomItem &r) { + [&resolveRes](Path, const DomItem &r) { resolveRes = r; return false; }, @@ -732,11 +900,11 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan if (!branchExhausted) visitTree( Path(), - [toFind, &toDos, iPath](Path, DomItem &item, bool) { + [&toFind, &toDos, iPath](Path, const DomItem &item, bool) { // avoid non directly attached? DomItem newItem = item[toFind]; if (newItem) - toDos.append({ newItem, iPath }); + toDos.append({ std::move(newItem), iPath }); return true; }, VisitOption::VisitSelf | VisitOption::Recurse @@ -760,7 +928,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan break; case PathCurrent::ObjChain: { bool cont = it.visitPrototypeChain( - [&toDos, iPath](DomItem &subEl) { + [&toDos, iPath](const DomItem &subEl) { toDos.append({ subEl, iPath }); return true; }, @@ -773,7 +941,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan } case PathCurrent::ScopeChain: { bool cont = it.visitScopeChain( - [&toDos, iPath](DomItem &subEl) { + [&toDos, iPath](const DomItem &subEl) { toDos.append({ subEl, iPath }); return true; }, @@ -903,7 +1071,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan } it.visitLookup( target, - [&toDos, iPath](DomItem &subEl) { + [&toDos, iPath](const DomItem &subEl) { toDos.append({ subEl, iPath }); return true; }, @@ -917,7 +1085,7 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan case Path::Kind::Any: visitTree( Path(), - [&toDos, iPath](Path, DomItem &item, bool) { + [&toDos, iPath](Path, const DomItem &item, bool) { toDos.append({ item, iPath }); return true; }, @@ -937,49 +1105,49 @@ bool DomItem::resolve(Path path, DomItem::Visitor visitor, ErrorHandler errorHan return true; } -DomItem DomItem::path(Path p, ErrorHandler errorHandler) +DomItem DomItem::path(const Path &p, const ErrorHandler &errorHandler) const { if (!p) return *this; DomItem res; - resolve(p, [&res](Path, DomItem it) { + resolve(p, [&res](const Path &, const DomItem &it) { res = it; return false; }, errorHandler); return res; } -DomItem DomItem::path(QString p, ErrorHandler errorHandler) +DomItem DomItem::path(const QString &p, const ErrorHandler &errorHandler) const { return path(Path::fromString(p, errorHandler)); } -DomItem DomItem::path(QStringView p, ErrorHandler errorHandler) +DomItem DomItem::path(QStringView p, const ErrorHandler &errorHandler) const { return path(Path::fromString(p, errorHandler)); } -QList<QString> DomItem::fields() +QList<QString> DomItem::fields() const { return visitEl([this](auto &&el) { return el->fields(*this); }); } -DomItem DomItem::field(QStringView name) +DomItem DomItem::field(QStringView name) const { return visitEl([this, name](auto &&el) { return el->field(*this, name); }); } -index_type DomItem::indexes() +index_type DomItem::indexes() const { return visitEl([this](auto &&el) { return el->indexes(*this); }); } -DomItem DomItem::index(index_type i) +DomItem DomItem::index(index_type i) const { return visitEl([this, i](auto &&el) { return el->index(*this, i); }); } -bool DomItem::visitIndexes(function_ref<bool(DomItem &)> visitor) +bool DomItem::visitIndexes(function_ref<bool(const DomItem &)> visitor) const { // use iterateDirectSubpathsConst instead? int nIndexes = indexes(); @@ -991,12 +1159,12 @@ bool DomItem::visitIndexes(function_ref<bool(DomItem &)> visitor) return true; } -QSet<QString> DomItem::keys() +QSet<QString> DomItem::keys() const { return visitEl([this](auto &&el) { return el->keys(*this); }); } -QStringList DomItem::sortedKeys() +QStringList DomItem::sortedKeys() const { QSet<QString> ks = keys(); QStringList sortedKs(ks.begin(), ks.end()); @@ -1004,15 +1172,16 @@ QStringList DomItem::sortedKeys() return sortedKs; } -DomItem DomItem::key(QString name) +DomItem DomItem::key(const QString &name) const { return visitEl([this, name](auto &&el) { return el->key(*this, name); }); } -bool DomItem::visitKeys(function_ref<bool(QString, DomItem &)> visitor) +bool DomItem::visitKeys(function_ref<bool(const QString &, const DomItem &)> visitor) const { // use iterateDirectSubpathsConst instead? - for (auto k : sortedKeys()) { + const QStringList keys = sortedKeys(); + for (const QString &k : keys) { DomItem v = key(k); if (!visitor(k, v)) return false; @@ -1020,10 +1189,10 @@ bool DomItem::visitKeys(function_ref<bool(QString, DomItem &)> visitor) return true; } -QList<DomItem> DomItem::values() +QList<DomItem> DomItem::values() const { QList<DomItem> res; - visitEl([this, &res](auto &&el) { + visitEl([this, &res](const auto &el) { return el->iterateDirectSubpathsConst( *this, [&res](const PathEls::PathComponent &, function_ref<DomItem()> item) { res.append(item()); @@ -1033,11 +1202,11 @@ QList<DomItem> DomItem::values() return res; } -void DomItem::writeOutPre(OutWriter &ow) +void DomItem::writeOutPre(OutWriter &ow) const { if (hasAnnotations()) { DomItem anns = field(Fields::annotations); - for (auto ann : anns.values()) { + for (const auto &ann : anns.values()) { if (ann.annotations().indexes() == 0) { ow.ensureNewline(); ann.writeOut(ow); @@ -1052,172 +1221,198 @@ void DomItem::writeOutPre(OutWriter &ow) ow.itemStart(*this); } -void DomItem::writeOut(OutWriter &ow) +void DomItem::writeOut(OutWriter &ow) const { writeOutPre(ow); visitEl([this, &ow](auto &&el) { el->writeOut(*this, ow); }); writeOutPost(ow); } -void DomItem::writeOutPost(OutWriter &ow) +void DomItem::writeOutPost(OutWriter &ow) const { ow.itemEnd(*this); } -DomItem DomItem::writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) +DomItem::WriteOutCheckResult DomItem::performWriteOutChecks(const DomItem &original, const DomItem &reformatted, + OutWriter &ow, + WriteOutChecks extraChecks) const +{ + QStringList dumped; + auto maybeDump = [&ow, extraChecks, &dumped](const DomItem &obj, QStringView objName) { + QString objDumpPath; + if (extraChecks & WriteOutCheck::DumpOnFailure) { + objDumpPath = QDir(QDir::tempPath()) + .filePath(objName.toString() + + QFileInfo(ow.lineWriter.fileName()).baseName() + + QLatin1String(".dump.json")); + obj.dump(objDumpPath); + dumped.append(objDumpPath); + } + return objDumpPath; + }; + auto dumpedDumper = [&dumped](const Sink &s) { + if (dumped.isEmpty()) + return; + s(u"\ndump: "); + for (const auto &dumpPath : dumped) { + s(u" "); + sinkEscaped(s, dumpPath); + } + }; + auto compare = [&maybeDump, &dumpedDumper, this](const DomItem &obj1, QStringView obj1Name, + const DomItem &obj2, QStringView obj2Name, + const FieldFilter &filter) { + const auto diffList = domCompareStrList(obj1, obj2, filter, DomCompareStrList::AllDiffs); + if (!diffList.isEmpty()) { + maybeDump(obj1, obj1Name); + maybeDump(obj2, obj2Name); + qCWarning(writeOutLog).noquote().nospace() + << obj2Name << " writeOut of " << this->canonicalFilePath() << " has changes:\n" + << diffList.join(QString()) << dumpedDumper; + return false; + } + return true; + }; + auto checkStability = [&maybeDump, &dumpedDumper, &dumped, &ow, + this](const QString &expected, const DomItem &obj, QStringView objName) { + LineWriter lw2([](QStringView) {}, ow.lineWriter.fileName(), ow.lineWriter.options()); + OutWriter ow2(lw2); + ow2.indentNextlines = true; + obj.writeOut(ow2); + ow2.eof(); + if (ow2.writtenStr != expected) { + DomItem fObj = this->fileObject(); + maybeDump(fObj, u"initial"); + maybeDump(obj, objName); + qCWarning(writeOutLog).noquote().nospace() + << objName << " non stable writeOut of " << this->canonicalFilePath() << ":" + << lineDiff(ow2.writtenStr, expected, 2) << dumpedDumper; + dumped.clear(); + return false; + } + return true; + }; + + if ((extraChecks & WriteOutCheck::UpdatedDomCompare) + && !compare(original, u"initial", reformatted, u"reformatted", + FieldFilter::noLocationFilter())) + return WriteOutCheckResult::Failed; + + if (extraChecks & WriteOutCheck::UpdatedDomStable) { + checkStability(ow.writtenStr, reformatted, u"reformatted"); + } + + if (extraChecks + & (WriteOutCheck::Reparse | WriteOutCheck::ReparseCompare | WriteOutCheck::ReparseStable)) { + DomItem newEnv = environment().makeCopy().item(); + std::shared_ptr<DomEnvironment> newEnvPtr = newEnv.ownerAs<DomEnvironment>(); + if (!newEnvPtr) + return WriteOutCheckResult::Failed; + + auto newFilePtr = std::make_shared<QmlFile>(canonicalFilePath(), ow.writtenStr); + if (!newFilePtr) + return WriteOutCheckResult::Failed; + newEnvPtr->addQmlFile(newFilePtr, AddOption::Overwrite); + + DomItem newFile = newEnv.copy(newFilePtr, Path()); + if (newFilePtr->isValid()) { + if (extraChecks & (WriteOutCheck::ReparseCompare | WriteOutCheck::ReparseStable)) { + newEnvPtr->populateFromQmlFile(newFile); + if ((extraChecks & WriteOutCheck::ReparseCompare) + && !compare(reformatted, u"reformatted", newFile, u"reparsed", + FieldFilter::compareNoCommentsFilter())) + return WriteOutCheckResult::Failed; + if ((extraChecks & WriteOutCheck::ReparseStable)) + checkStability(ow.writtenStr, newFile, u"reparsed"); + } + } else { + const auto iterateErrors = [&newFile](const Sink &s) { + newFile.iterateErrors( + [s](const DomItem &, const ErrorMessage &msg) { + s(u"\n "); + msg.dump(s); + return true; + }, + true); + s(u"\n"); // extra empty line at the end... + }; + qCWarning(writeOutLog).noquote().nospace() + << "writeOut of " << canonicalFilePath() + << " created invalid code:\n----------\n" + << ow.writtenStr << "\n----------" << iterateErrors; + return WriteOutCheckResult::Failed; + } + } + return WriteOutCheckResult::Success; +} + +/*! + \internal + Performes WriteOut of the FileItem and verifies the consistency of the DOM structure. + + OutWriter is essentially a visitor traversing the DOM structure, starting from + the current item representing a FileItem. + While traversing it might be saving some intermediate information, used later for restoring + written out item. Restoration is needed to validate that the DOM structure of the written item + has not changed. +*/ +bool DomItem::writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const { ow.indentNextlines = true; writeOut(ow); ow.eof(); - DomItem fObj = fileObject(); - DomItem copy = ow.updatedFile(fObj); - if (extraChecks & WriteOutCheck::All) { - QStringList dumped; - auto maybeDump = [&ow, extraChecks, &dumped](DomItem &obj, QStringView objName) { - QString objDumpPath; - if (extraChecks & WriteOutCheck::DumpOnFailure) { - objDumpPath = QDir(QDir::tempPath()) - .filePath(objName.toString() - + QFileInfo(ow.lineWriter.fileName()).baseName() - + QLatin1String(".dump.json")); - obj.dump(objDumpPath); - dumped.append(objDumpPath); - } - return objDumpPath; - }; - auto dumpedDumper = [&dumped](Sink s) { - if (dumped.isEmpty()) - return; - s(u"\ndump: "); - for (auto dumpPath : dumped) { - s(u" "); - sinkEscaped(s, dumpPath); - } - }; - auto compare = [&maybeDump, &dumpedDumper, this](DomItem &obj1, QStringView obj1Name, - DomItem &obj2, QStringView obj2Name, - const FieldFilter &filter) { - if (!domCompareStrList(obj1, obj2, filter).isEmpty()) { - maybeDump(obj1, obj1Name); - maybeDump(obj2, obj2Name); - qCWarning(writeOutLog).noquote().nospace() - << obj2Name << " writeOut of " << this->canonicalFilePath() - << " has changes:\n" - << domCompareStrList(obj1, obj2, filter, DomCompareStrList::AllDiffs) - .join(QString()) - << dumpedDumper; - return false; - } - return true; - }; - auto checkStability = [&maybeDump, &dumpedDumper, &dumped, &ow, - this](QString expected, DomItem &obj, QStringView objName) { - LineWriter lw2([](QStringView) {}, ow.lineWriter.fileName(), ow.lineWriter.options()); - OutWriter ow2(lw2); - ow2.indentNextlines = true; - obj.writeOut(ow2); - ow2.eof(); - if (ow2.writtenStr != expected) { - DomItem fObj = this->fileObject(); - maybeDump(fObj, u"initial"); - maybeDump(obj, objName); - qCWarning(writeOutLog).noquote().nospace() - << objName << " non stable writeOut of " << this->canonicalFilePath() << ":" - << lineDiff(ow2.writtenStr, expected, 2) << dumpedDumper; - dumped.clear(); - return false; - } - return true; - }; - if ((extraChecks & WriteOutCheck::UpdatedDomCompare) - && !compare(fObj, u"initial", copy, u"reformatted", FieldFilter::noLocationFilter())) - return DomItem(); - if (extraChecks & WriteOutCheck::UpdatedDomStable) - checkStability(ow.writtenStr, copy, u"reformatted"); - if (extraChecks - & (WriteOutCheck::Reparse | WriteOutCheck::ReparseCompare - | WriteOutCheck::ReparseStable)) { - DomItem newEnv = environment().makeCopy().item(); - if (std::shared_ptr<DomEnvironment> newEnvPtr = newEnv.ownerAs<DomEnvironment>()) { - auto newFilePtr = std::make_shared<QmlFile>( - canonicalFilePath(), ow.writtenStr); - newEnvPtr->addQmlFile(newFilePtr, AddOption::Overwrite); - DomItem newFile = newEnv.copy(newFilePtr, Path()); - if (newFilePtr->isValid()) { - if (extraChecks - & (WriteOutCheck::ReparseCompare | WriteOutCheck::ReparseStable)) { - MutableDomItem newFileMutable(newFile); - createDom(newFileMutable); - if ((extraChecks & WriteOutCheck::ReparseCompare) - && !compare(copy, u"reformatted", newFile, u"reparsed", - FieldFilter::compareNoCommentsFilter())) - return DomItem(); - if ((extraChecks & WriteOutCheck::ReparseStable)) - checkStability(ow.writtenStr, newFile, u"reparsed"); - } - } else { - qCWarning(writeOutLog).noquote().nospace() - << "writeOut of " << canonicalFilePath() - << " created invalid code:\n----------\n" - << ow.writtenStr << "\n----------" << [&newFile](Sink s) { - newFile.iterateErrors( - [s](DomItem, ErrorMessage msg) { - s(u"\n "); - msg.dump(s); - return true; - }, - true); - s(u"\n"); // extra empty line at the end... - }; - return DomItem(); - } - } - } - } - return copy; + + auto currentFileItem = fileObject(); + auto writtenFileItem = ow.restoreWrittenFileItem(currentFileItem); + WriteOutCheckResult result = WriteOutCheckResult::Success; + if (extraChecks & WriteOutCheck::All) + result = performWriteOutChecks(currentFileItem, writtenFileItem, ow, extraChecks); + return result == WriteOutCheckResult::Success ? bool(writtenFileItem) : false; } -DomItem DomItem::writeOut(QString path, int nBackups, const LineWriterOptions &options, - FileWriter *fw, WriteOutChecks extraChecks) +bool DomItem::writeOut(const QString &path, int nBackups, const LineWriterOptions &options, + FileWriter *fw, WriteOutChecks extraChecks) const { - DomItem res = *this; - DomItem copy; FileWriter localFw; if (!fw) fw = &localFw; - switch (fw->write( + auto status = fw->write( path, - [this, path, ©, &options, extraChecks](QTextStream &ts) { + [this, path, &options, extraChecks](QTextStream &ts) { LineWriter lw([&ts](QStringView s) { ts << s; }, path, options); OutWriter ow(lw); - copy = writeOutForFile(ow, extraChecks); - return bool(copy); + return writeOutForFile(ow, extraChecks); }, - nBackups)) { + nBackups); + switch (status) { + case FileWriter::Status::DidWrite: + case FileWriter::Status::SkippedEqual: + return true; case FileWriter::Status::ShouldWrite: case FileWriter::Status::SkippedDueToFailure: qCWarning(writeOutLog) << "failure reformatting " << path; - break; - case FileWriter::Status::DidWrite: - case FileWriter::Status::SkippedEqual: - res = copy; - break; + return false; + default: + qCWarning(writeOutLog) << "Unknown FileWriter::Status "; + Q_ASSERT(false); + return false; } - return res; } -bool DomItem::isCanonicalChild(DomItem &item) +bool DomItem::isCanonicalChild(const DomItem &item) const { + bool isChild = false; if (item.isOwningItem()) { - return canonicalPath() == item.canonicalPath().dropTail(); + isChild = canonicalPath() == item.canonicalPath().dropTail(); } else { DomItem itemOw = item.owner(); DomItem selfOw = owner(); - return itemOw == selfOw && item.pathFromOwner().dropTail() == pathFromOwner(); + isChild = itemOw == selfOw && item.pathFromOwner().dropTail() == pathFromOwner(); } + return isChild; } -bool DomItem::hasAnnotations() +bool DomItem::hasAnnotations() const { bool hasAnnotations = false; DomType iKind = internalKind(); @@ -1248,28 +1443,56 @@ bool DomItem::hasAnnotations() return hasAnnotations; } -bool DomItem::visitTree(Path basePath, DomItem::ChildrenVisitor visitor, VisitOptions options, - DomItem::ChildrenVisitor openingVisitor, - DomItem::ChildrenVisitor closingVisitor) +/*! + \internal + \brief Visits recursively all the children of this item using the given visitors. + + First, the visitor is called and can continue or exit the visit by returning true or false. + + Second, the openingVisitor is called and controls if the children of the current item needs to + be visited or not by returning true or false. In either case, the visitation of all the other + siblings is not affected. If both visitor and openingVisitor returned true, then the childrens of + the current item will be recursively visited. + + Finally, after all the children were visited by visitor and openingVisitor, the closingVisitor + is called. Its return value is currently ignored. + + Compared to the AST::Visitor*, openingVisitor and closingVisitor are called in the same order as + the visit() and endVisit()-calls. + + Filtering allows to not visit certain part of the trees, and is checked before(!) the lazy child + is instantiated via its lambda. For example, visiting propertyInfos or defaultPropertyname takes + a lot of time because it resolves and collects all properties inherited from base types, and + might not even be relevant for the visitors. + */ +bool DomItem::visitTree(const Path &basePath, DomItem::ChildrenVisitor visitor, + VisitOptions options, DomItem::ChildrenVisitor openingVisitor, + DomItem::ChildrenVisitor closingVisitor, const FieldFilter &filter) const { if (!*this) return true; if (options & VisitOption::VisitSelf && !visitor(basePath, *this, true)) return false; - if (!openingVisitor(basePath, *this, true)) + if (options & VisitOption::VisitSelf && !openingVisitor(basePath, *this, true)) return true; - auto atEnd = qScopeGuard( - [closingVisitor, basePath, this]() { closingVisitor(basePath, *this, true); }); - return visitEl([this, basePath, visitor, openingVisitor, closingVisitor, options](auto &&el) { + auto atEnd = qScopeGuard([closingVisitor, basePath, this, options]() { + if (options & VisitOption::VisitSelf) { + closingVisitor(basePath, *this, true); + } + }); + return visitEl([this, basePath, visitor, openingVisitor, closingVisitor, options, + &filter](auto &&el) { return el->iterateDirectSubpathsConst( *this, - [this, basePath, visitor, openingVisitor, closingVisitor, - options](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) { + [this, basePath, visitor, openingVisitor, closingVisitor, options, + &filter](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) { Path pNow; if (!(options & VisitOption::NoPath)) { pNow = basePath; pNow = pNow.appendComponent(c); } + if (!filter(*this, c, DomItem{})) + return true; DomItem item = itemF(); bool directChild = isCanonicalChild(item); if (!directChild && !(options & VisitOption::VisitAdopted)) @@ -1285,16 +1508,81 @@ bool DomItem::visitTree(Path basePath, DomItem::ChildrenVisitor visitor, VisitOp closingVisitor(pNow, item, directChild); } else { return item.visitTree(pNow, visitor, options | VisitOption::VisitSelf, - openingVisitor, closingVisitor); + openingVisitor, closingVisitor, filter); } return true; }); }); } +static bool visitPrototypeIndex(QList<DomItem> &toDo, const DomItem ¤t, + const DomItem &derivedFromPrototype, const ErrorHandler &h, + QList<Path> *visitedRefs, VisitPrototypesOptions options, + const DomItem &prototype) +{ + Path elId = prototype.canonicalPath(); + if (visitedRefs->contains(elId)) + return true; + else + visitedRefs->append(elId); + QList<DomItem> protos = prototype.getAll(h, visitedRefs); + if (protos.isEmpty()) { + if (std::shared_ptr<DomEnvironment> envPtr = + derivedFromPrototype.environment().ownerAs<DomEnvironment>()) + if (!(envPtr->options() & DomEnvironment::Option::NoDependencies)) + derivedFromPrototype.myErrors() + .warning(derivedFromPrototype.tr("could not resolve prototype %1 (%2)") + .arg(current.canonicalPath().toString(), + prototype.field(Fields::referredObjectPath) + .value() + .toString())) + .withItem(derivedFromPrototype) + .handle(h); + } else { + if (protos.size() > 1) { + QStringList protoPaths; + for (const DomItem &p : protos) + protoPaths.append(p.canonicalPath().toString()); + derivedFromPrototype.myErrors() + .warning(derivedFromPrototype + .tr("Multiple definitions found, using first only, resolving " + "prototype %1 (%2): %3") + .arg(current.canonicalPath().toString(), + prototype.field(Fields::referredObjectPath) + .value() + .toString(), + protoPaths.join(QLatin1String(", ")))) + .withItem(derivedFromPrototype) + .handle(h); + } + int nProtos = 1; // change to protos.length() to use all prototypes + // (sloppier) + for (int i = nProtos; i != 0;) { + DomItem proto = protos.at(--i); + if (proto.internalKind() == DomType::Export) { + if (!(options & VisitPrototypesOption::ManualProceedToScope)) + proto = proto.proceedToScope(h, visitedRefs); + toDo.append(proto); + } else if (proto.internalKind() == DomType::QmlObject + || proto.internalKind() == DomType::QmlComponent) { + toDo.append(proto); + } else { + derivedFromPrototype.myErrors() + .warning(derivedFromPrototype.tr("Unexpected prototype type %1 (%2)") + .arg(current.canonicalPath().toString(), + prototype.field(Fields::referredObjectPath) + .value() + .toString())) + .withItem(derivedFromPrototype) + .handle(h); + } + } + } + return true; +} -bool DomItem::visitPrototypeChain(function_ref<bool(DomItem &)> visitor, - VisitPrototypesOptions options, ErrorHandler h, - QSet<quintptr> *visited, QList<Path> *visitedRefs) +bool DomItem::visitPrototypeChain(function_ref<bool(const DomItem &)> visitor, + VisitPrototypesOptions options, const ErrorHandler &h, + QSet<quintptr> *visited, QList<Path> *visitedRefs) const { QSet<quintptr> visitedLocal; if (!visited) @@ -1330,72 +1618,16 @@ bool DomItem::visitPrototypeChain(function_ref<bool(DomItem &)> visitor, return false; shouldVisit = true; current.field(Fields::prototypes) - .visitIndexes([&toDo, ¤t, this, &h, visitedRefs, options](DomItem &el) { - Path elId = el.canonicalPath(); - if (visitedRefs->contains(elId)) - return true; - else - visitedRefs->append(elId); - QList<DomItem> protos = el.getAll(h, visitedRefs); - if (protos.isEmpty()) { - if (std::shared_ptr<DomEnvironment> envPtr = - environment().ownerAs<DomEnvironment>()) - if (!(envPtr->options() & DomEnvironment::Option::NoDependencies)) - myErrors() - .warning(tr("could not resolve prototype %1 (%2)") - .arg(current.canonicalPath().toString(), - el.field(Fields::referredObjectPath) - .value() - .toString())) - .withItem(*this) - .handle(h); - } else { - if (protos.size() > 1) { - QStringList protoPaths; - for (DomItem &p : protos) - protoPaths.append(p.canonicalPath().toString()); - myErrors() - .warning(tr("Multiple definitions found, using first only, " - "resolving prototype %1 (%2): %3") - .arg(current.canonicalPath().toString(), - el.field(Fields::referredObjectPath) - .value() - .toString(), - protoPaths.join(QLatin1String(", ")))) - .withItem(*this) - .handle(h); - } - int nProtos = 1; // change to protos.length() to us all prototypes found - // (sloppier) - for (int i = nProtos; i != 0;) { - DomItem proto = protos.at(--i); - if (proto.internalKind() == DomType::Export) { - if (!(options & VisitPrototypesOption::ManualProceedToScope)) - proto = proto.proceedToScope(h, visitedRefs); - toDo.append(proto); - } else if (proto.internalKind() == DomType::QmlObject) { - toDo.append(proto); - } else { - myErrors() - .warning(tr("Unexpected prototype type %1 (%2)") - .arg(current.canonicalPath().toString(), - el.field(Fields::referredObjectPath) - .value() - .toString())) - .withItem(*this) - .handle(h); - } - } - } - return true; + .visitIndexes([&toDo, ¤t, this, &h, visitedRefs, options](const DomItem &el) { + return visitPrototypeIndex(toDo, current, *this, h, visitedRefs, options, el); }); } return true; } -bool DomItem::visitDirectAccessibleScopes(function_ref<bool(DomItem &)> visitor, - VisitPrototypesOptions options, ErrorHandler h, - QSet<quintptr> *visited, QList<Path> *visitedRefs) +bool DomItem::visitDirectAccessibleScopes( + function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options, + const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const { // these are the scopes one can access with the . operator from the current location // but currently not the attached types, which we should @@ -1440,9 +1672,9 @@ bool DomItem::visitDirectAccessibleScopes(function_ref<bool(DomItem &)> visitor, * visit the values JS reaches accessing a type directly: the values if it is a singleton or the * attached type */ -bool DomItem::visitStaticTypePrototypeChains(function_ref<bool(DomItem &)> visitor, - VisitPrototypesOptions options, ErrorHandler h, - QSet<quintptr> *visited, QList<Path> *visitedRefs) +bool DomItem::visitStaticTypePrototypeChains( + function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options, + const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const { QSet<quintptr> visitedLocal; if (!visited) @@ -1460,8 +1692,27 @@ bool DomItem::visitStaticTypePrototypeChains(function_ref<bool(DomItem &)> visit return true; } -bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptions options, - ErrorHandler h, QSet<quintptr> *visited, QList<Path> *visitedRefs) +/*! + \brief Let the visitor visit the Dom Tree hierarchy of this DomItem. + */ +bool DomItem::visitUp(function_ref<bool(const DomItem &)> visitor) const +{ + Path p = canonicalPath(); + while (p.length() > 0) { + DomItem current = top().path(p); + if (!visitor(current)) + return false; + p = p.dropTail(); + } + return true; +} + +/*! + \brief Let the visitor visit the QML scope hierarchy of this DomItem. + */ +bool DomItem::visitScopeChain( + function_ref<bool(const DomItem &)> visitor, LookupOptions options, const ErrorHandler &h, + QSet<quintptr> *visited, QList<Path> *visitedRefs) const { QSet<quintptr> visitedLocal; if (!visited) @@ -1478,6 +1729,7 @@ bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptio bool visitFirst = !(options & LookupOption::SkipFirstScope); bool visitCurrent = visitFirst; bool first = true; + QSet<quintptr> alreadyAddedComponentMaps; while (!toDo.isEmpty()) { DomItem current = toDo.takeLast(); if (visited->contains(current.id())) @@ -1505,7 +1757,7 @@ bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptio if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf)) toDo.append(next); break; - case DomType::QmlComponent: // ids/attached type + case DomType::QmlComponent: { // ids/attached type if ((options & LookupOption::Strict) == 0) { if (DomItem comp = current.field(Fields::nextComponent)) toDo.append(comp); @@ -1517,8 +1769,25 @@ bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptio } if (DomItem next = current.scope(FilterUpOptions::ReturnOuterNoSelf)) toDo.append(next); + + DomItem owner = current.owner(); + Path pathToComponentMap = current.pathFromOwner().dropTail(2); + DomItem componentMap = owner.path(pathToComponentMap); + if (alreadyAddedComponentMaps.contains(componentMap.id())) + break; + alreadyAddedComponentMaps.insert(componentMap.id()); + const auto keys = componentMap.keys(); + for (const QString &x : keys) { + DomItem componentList = componentMap.key(x); + for (int i = 0; i < componentList.indexes(); ++i) { + DomItem component = componentList.index(i); + if (component != current && !visited->contains(component.id())) + toDo.append(component); + } + } first = false; break; + } case DomType::QmlFile: // subComponents, imported types if (DomItem iScope = current.field(Fields::importScope)) // treat file as a separate scope? @@ -1558,118 +1827,14 @@ bool DomItem::visitScopeChain(function_ref<bool(DomItem &)> visitor, LookupOptio return true; } -QSet<QString> DomItem::localSymbolNames(LocalSymbolsTypes typeFilter) +bool DomItem::visitLookup1( + const QString &symbolName, function_ref<bool(const DomItem &)> visitor, LookupOptions opts, + const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const { - QSet<QString> res; - if (typeFilter == LocalSymbolsType::None) - return res; - switch (internalKind()) { - case DomType::QmlObject: - if (typeFilter & LocalSymbolsType::Attributes) { - res += propertyDefs().keys(); - res += bindings().keys(); - } - if (typeFilter & LocalSymbolsType::Methods) { - if ((typeFilter & LocalSymbolsType::Methods) == LocalSymbolsType::Methods) { - res += methods().keys(); - } else { - bool shouldAddSignals = bool(typeFilter & LocalSymbolsType::Signals); - if (const QmlObject *objPtr = as<QmlObject>()) { - auto methods = objPtr->methods(); - for (auto it = methods.cbegin(); it != methods.cend(); ++it) { - if (bool(it.value().methodType == MethodInfo::MethodType::Signal) - == shouldAddSignals) - res += it.key(); - } - } - } - } - break; - case DomType::ScriptExpression: - // to do - break; - case DomType::QmlComponent: - if (typeFilter & LocalSymbolsType::Ids) - res += ids().keys(); - break; - case DomType::QmlFile: // subComponents, imported types - if (typeFilter & LocalSymbolsType::Components) { - DomItem comps = field(Fields::components); - for (auto k : comps.keys()) - if (!k.isEmpty()) - res.insert(k); - } - break; - case DomType::ImportScope: { - const ImportScope *currentPtr = as<ImportScope>(); - if (typeFilter & LocalSymbolsType::Types) { - if ((typeFilter & LocalSymbolsType::Types) == LocalSymbolsType::Types) { - res += currentPtr->importedNames(*this); - } else { - bool qmlTypes = bool(typeFilter & LocalSymbolsType::QmlTypes); - for (const QString &typeName : currentPtr->importedNames(*this)) { - if ((!typeName.isEmpty() && typeName.at(0).isUpper()) == qmlTypes) - res += typeName; - } - } - } - if (typeFilter & LocalSymbolsType::Namespaces) { - for (const auto &k : currentPtr->subImports().keys()) - res.insert(k); - } - break; - } - case DomType::QmltypesComponent: - case DomType::JsResource: - case DomType::GlobalComponent: - if (typeFilter & LocalSymbolsType::Globals) - res += enumerations().keys(); - break; - case DomType::MethodInfo: { - if (typeFilter & LocalSymbolsType::MethodParameters) { - DomItem params = field(Fields::parameters); - params.visitIndexes([&res](DomItem &p) { - const MethodParameter *pPtr = p.as<MethodParameter>(); - res.insert(pPtr->name); - return true; - }); - } - break; - } - default: - break; - } - return res; -} - -bool DomItem::visitLookup1(QString symbolName, function_ref<bool(DomItem &)> visitor, - LookupOptions opts, ErrorHandler h, QSet<quintptr> *visited, - QList<Path> *visitedRefs) -{ - bool typeLookupInQmlFile = symbolName.size() > 1 && symbolName.at(0).isUpper() - && fileObject().internalKind() == DomType::QmlFile; - if (typeLookupInQmlFile) { - // shortcut to lookup types (scope chain would find them too, but after looking - // the prototype chain) - DomItem importScope = fileObject().field(Fields::importScope); - if (const ImportScope *importScopePtr = importScope.as<ImportScope>()) { - if (importScopePtr->subImports().contains(symbolName)) { - DomItem subItem = importScope.field(Fields::qualifiedImports).key(symbolName); - if (!visitor(subItem)) - return false; - } - QList<DomItem> types = importScopePtr->importedItemsWithName(importScope, symbolName); - for (DomItem &t : types) { - if (!visitor(t)) - return false; - } - } - return true; - } return visitScopeChain( - [symbolName, visitor](DomItem &obj) { + [symbolName, visitor](const DomItem &obj) { return obj.visitLocalSymbolsNamed(symbolName, - [visitor](DomItem &el) { return visitor(el); }); + [visitor](const DomItem &el) { return visitor(el); }); }, opts, h, visited, visitedRefs); } @@ -1680,7 +1845,7 @@ class CppTypeInfo public: CppTypeInfo() = default; - static CppTypeInfo fromString(QStringView target, ErrorHandler h = nullptr) + static CppTypeInfo fromString(QStringView target, const ErrorHandler &h = nullptr) { CppTypeInfo res; QRegularExpression reTarget = QRegularExpression(QRegularExpression::anchoredPattern( @@ -1719,9 +1884,95 @@ public: bool isList = false; }; -bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor, - LookupType lookupType, LookupOptions opts, ErrorHandler errorHandler, - QSet<quintptr> *visited, QList<Path> *visitedRefs) +static bool visitForLookupType(const DomItem &el, LookupType lookupType, + function_ref<bool(const DomItem &)> visitor) +{ + bool correctType = false; + DomType iType = el.internalKind(); + switch (lookupType) { + case LookupType::Binding: + correctType = (iType == DomType::Binding); + break; + case LookupType::Method: + correctType = (iType == DomType::MethodInfo); + break; + case LookupType::Property: + correctType = (iType == DomType::PropertyDefinition || iType == DomType::Binding); + break; + case LookupType::PropertyDef: + correctType = (iType == DomType::PropertyDefinition); + break; + case LookupType::Type: + correctType = (iType == DomType::Export); // accept direct QmlObject ref? + break; + default: + Q_ASSERT(false); + break; + } + if (correctType) + return visitor(el); + return true; +} + +static bool visitQualifiedNameLookup( + const DomItem &newIt, const QStringList &subpath, + function_ref<bool(const DomItem &)> visitor, LookupType lookupType, + const ErrorHandler &errorHandler, QList<Path> *visitedRefs) +{ + QVector<ResolveToDo> lookupToDos( + { ResolveToDo{ newIt, 1 } }); // invariant: always increase pathIndex to guarantee + // end even with only partial visited match + QList<QSet<quintptr>> lookupVisited(subpath.size() + 1); + while (!lookupToDos.isEmpty()) { + ResolveToDo tNow = lookupToDos.takeFirst(); + auto vNow = qMakePair(tNow.item.id(), tNow.pathIndex); + DomItem subNow = tNow.item; + int iSubPath = tNow.pathIndex; + Q_ASSERT(iSubPath < subpath.size()); + QString subPathNow = subpath[iSubPath++]; + DomItem scope = subNow.proceedToScope(); + if (iSubPath < subpath.size()) { + if (vNow.first != 0) { + if (lookupVisited[vNow.second].contains(vNow.first)) + continue; + else + lookupVisited[vNow.second].insert(vNow.first); + } + if (scope.internalKind() == DomType::QmlObject) + scope.visitDirectAccessibleScopes( + [&lookupToDos, &subPathNow, iSubPath](const DomItem &el) { + return el.visitLocalSymbolsNamed( + subPathNow, [&lookupToDos, iSubPath](const DomItem &subEl) { + lookupToDos.append({ subEl, iSubPath }); + return true; + }); + }, + VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]), + visitedRefs); + } else { + bool cont = scope.visitDirectAccessibleScopes( + [&visitor, &subPathNow, lookupType](const DomItem &el) -> bool { + if (lookupType == LookupType::Symbol) + return el.visitLocalSymbolsNamed(subPathNow, visitor); + else + return el.visitLocalSymbolsNamed( + subPathNow, [lookupType, &visitor](const DomItem &el) -> bool { + return visitForLookupType(el, lookupType, visitor); + }); + }, + VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]), + visitedRefs); + if (!cont) + return false; + } + } + return true; +} + +bool DomItem::visitLookup( + const QString &target, function_ref<bool(const DomItem &)> visitor, LookupType lookupType, + LookupOptions opts, const ErrorHandler &errorHandler, QSet<quintptr> *visited, + QList<Path> *visitedRefs) const { if (target.isEmpty()) return true; @@ -1738,101 +1989,10 @@ bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor, } else { return visitLookup1( subpath.at(0), - [&subpath, visitor, lookupType, &errorHandler, visitedRefs](DomItem &newIt) { - QVector<ResolveToDo> lookupToDos({ ResolveToDo { - newIt, 1 } }); // invariant: always increase pathIndex to guarantee - // end even with only partial visited match - QList<QSet<quintptr>> lookupVisited(subpath.size() + 1); - while (!lookupToDos.isEmpty()) { - ResolveToDo tNow = lookupToDos.takeFirst(); - auto vNow = qMakePair(tNow.item.id(), tNow.pathIndex); - DomItem subNow = tNow.item; - int iSubPath = tNow.pathIndex; - Q_ASSERT(iSubPath < subpath.size()); - QString subPathNow = subpath[iSubPath++]; - DomItem scope = subNow.proceedToScope(); - if (iSubPath < subpath.size()) { - if (vNow.first != 0) { - if (lookupVisited[vNow.second].contains(vNow.first)) - continue; - else - lookupVisited[vNow.second].insert(vNow.first); - } - if (scope.internalKind() == DomType::QmlObject) - scope.visitDirectAccessibleScopes( - [&lookupToDos, subPathNow, iSubPath](DomItem &el) { - return el.visitLocalSymbolsNamed( - subPathNow, - [&lookupToDos, iSubPath](DomItem &subEl) { - lookupToDos.append({ subEl, iSubPath }); - return true; - }); - }, - VisitPrototypesOption::Normal, errorHandler, - &(lookupVisited[vNow.second]), visitedRefs); - } else { - bool cont = scope.visitDirectAccessibleScopes( - [&visitor, subPathNow, lookupType](DomItem &el) -> bool { - if (lookupType == LookupType::Symbol) - return el.visitLocalSymbolsNamed(subPathNow, - visitor); - else - return el.visitLocalSymbolsNamed( - subPathNow, - [lookupType, - &visitor](DomItem &el) -> bool { - bool correctType = false; - DomType iType = el.internalKind(); - switch (lookupType) { - case LookupType::Binding: - correctType = - (iType == DomType::Binding); - break; - case LookupType::Method: - correctType = - (iType - == DomType::MethodInfo); - break; - case LookupType::Property: - correctType = - (iType - == DomType:: - PropertyDefinition - || iType - == DomType:: - Binding); - break; - case LookupType::PropertyDef: - correctType = - (iType - == DomType:: - PropertyDefinition); - break; - case LookupType::Type: - correctType = - (iType - == DomType:: - Export); // accept - // direct - // QmlObject - // ref? - break; - default: - Q_ASSERT(false); - break; - } - if (correctType) - return visitor(el); - return true; - }); - }, - VisitPrototypesOption::Normal, errorHandler, - &(lookupVisited[vNow.second]), visitedRefs); - if (!cont) - return false; - } - } - return true; + [&subpath, visitor, lookupType, &errorHandler, + visitedRefs](const DomItem &newIt) -> bool { + return visitQualifiedNameLookup(newIt, subpath, visitor, lookupType, + errorHandler, visitedRefs); }, opts, errorHandler, visited, visitedRefs); } @@ -1847,8 +2007,8 @@ bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor, } if (localQmltypes) { if (DomItem localTypes = localQmltypes.field(Fields::components).key(baseTarget)) { - bool cont = localTypes.visitIndexes([&visitor](DomItem &els) { - return els.visitIndexes([&visitor](DomItem &el) { + bool cont = localTypes.visitIndexes([&visitor](const DomItem &els) { + return els.visitIndexes([&visitor](const DomItem &el) { if (DomItem obj = el.field(Fields::objects).index(0)) return visitor(obj); return true; @@ -1859,10 +2019,10 @@ bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor, } } DomItem qmltypes = environment().field(Fields::qmltypesFileWithPath); - return qmltypes.visitKeys([baseTarget, &visitor](QString, DomItem &els) { + return qmltypes.visitKeys([baseTarget, &visitor](const QString &, const DomItem &els) { DomItem comps = els.field(Fields::currentItem).field(Fields::components).key(baseTarget); - return comps.visitIndexes([&visitor](DomItem &el) { + return comps.visitIndexes([&visitor](const DomItem &el) { if (DomItem obj = el.field(Fields::objects).index(0)) return visitor(obj); return true; @@ -1875,7 +2035,15 @@ bool DomItem::visitLookup(QString target, function_ref<bool(DomItem &)> visitor, return true; } -DomItem DomItem::proceedToScope(ErrorHandler h, QList<Path> *visitedRefs) +/*! + \internal + \brief Dereference DomItems pointing to other DomItems. + + Dereferences DomItems with internalKind being References, Export and Id. + Also does multiple rounds of resolving for nested DomItems. + Prefer this over \l {DomItem::get}. + */ +DomItem DomItem::proceedToScope(const ErrorHandler &h, QList<Path> *visitedRefs) const { // follow references, resolve exports DomItem current = *this; @@ -1900,13 +2068,13 @@ DomItem DomItem::proceedToScope(ErrorHandler h, QList<Path> *visitedRefs) return DomItem(); } -QList<DomItem> DomItem::lookup(QString symbolName, LookupType type, LookupOptions opts, - ErrorHandler errorHandler) +QList<DomItem> DomItem::lookup(const QString &symbolName, LookupType type, LookupOptions opts, + const ErrorHandler &errorHandler) const { QList<DomItem> res; visitLookup( symbolName, - [&res](DomItem &el) { + [&res](const DomItem &el) { res.append(el); return true; }, @@ -1914,13 +2082,13 @@ QList<DomItem> DomItem::lookup(QString symbolName, LookupType type, LookupOption return res; } -DomItem DomItem::lookupFirst(QString symbolName, LookupType type, LookupOptions opts, - ErrorHandler errorHandler) +DomItem DomItem::lookupFirst(const QString &symbolName, LookupType type, LookupOptions opts, + const ErrorHandler &errorHandler) const { DomItem res; visitLookup( symbolName, - [&res](DomItem &el) { + [&res](const DomItem &el) { res = el; return false; }, @@ -1928,75 +2096,81 @@ DomItem DomItem::lookupFirst(QString symbolName, LookupType type, LookupOptions return res; } -quintptr DomItem::id() +quintptr DomItem::id() const { return visitEl([](auto &&b) { return b->id(); }); } -Path DomItem::pathFromOwner() +Path DomItem::pathFromOwner() const { return visitEl([this](auto &&e) { return e->pathFromOwner(*this); }); } -QString DomItem::canonicalFilePath() +QString DomItem::canonicalFilePath() const { return visitEl([this](auto &&e) { return e->canonicalFilePath(*this); }); } -DomItem DomItem::fileLocationsTree() +DomItem DomItem::fileLocationsTree() const { if (DomItem l = field(Fields::fileLocationsTree)) return l; - auto res = FileLocations::findAttachedInfo(*this, AttachedInfo::FindOption::SetFoundTreePath); - if (res && res.foundTreePath.value()) { - return copy(res.foundTree, res.foundTreePath.value()); + auto res = FileLocations::findAttachedInfo(*this); + if (res && res.foundTreePath) { + return copy(res.foundTree, res.foundTreePath); } return DomItem(); } -DomItem DomItem::fileLocations() +DomItem DomItem::fileLocations() const { return fileLocationsTree().field(Fields::infoItem); } -MutableDomItem DomItem::makeCopy(DomItem::CopyOption option) +MutableDomItem DomItem::makeCopy(DomItem::CopyOption option) const { if (m_kind == DomType::Empty) return MutableDomItem(); DomItem o = owner(); if (option == CopyOption::EnvDisconnected) { - DomItem newItem = std::visit( - [this, &o](auto &&el) { - auto copyPtr = el->makeCopy(o); - return DomItem(m_top, copyPtr, m_ownerPath, copyPtr.get()); - }, - *m_owner); + DomItem newItem = std::visit([this, &o](auto &&el) { + if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) { + return DomItem(); + } else { + auto copyPtr = el->makeCopy(o); + return DomItem(m_top, copyPtr, m_ownerPath, copyPtr.get()); + } + }, m_owner); return MutableDomItem(newItem.path(pathFromOwner())); } DomItem env = environment(); std::shared_ptr<DomEnvironment> newEnvPtr; if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) { - newEnvPtr = std::make_shared<DomEnvironment>( - envPtr, envPtr->loadPaths(), envPtr->options()); + newEnvPtr = std::make_shared<DomEnvironment>(envPtr, envPtr->loadPaths(), envPtr->options(), + envPtr->domCreationOptions()); DomBase *eBase = envPtr.get(); - if (std::holds_alternative<DomEnvironment *>(m_element) && eBase - && std::get<DomEnvironment *>(m_element) == eBase) + if (std::holds_alternative<const DomEnvironment *>(m_element) && eBase + && std::get<const DomEnvironment *>(m_element) == eBase) return MutableDomItem(DomItem(newEnvPtr)); } else if (std::shared_ptr<DomUniverse> univPtr = top().ownerAs<DomUniverse>()) { newEnvPtr = std::make_shared<DomEnvironment>( QStringList(), DomEnvironment::Option::SingleThreaded | DomEnvironment::Option::NoDependencies, - univPtr); + DomCreationOption::None, univPtr); } else { Q_ASSERT(false); return {}; } DomItem newItem = std::visit( [this, newEnvPtr, &o](auto &&el) { - auto copyPtr = el->makeCopy(o); - return DomItem(newEnvPtr, copyPtr, m_ownerPath, copyPtr.get()); + if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) { + return DomItem(); + } else { + auto copyPtr = el->makeCopy(o); + return DomItem(newEnvPtr, copyPtr, m_ownerPath, copyPtr.get()); + } }, - *m_owner); + m_owner); switch (o.internalKind()) { case DomType::QmlDirectory: @@ -2037,7 +2211,7 @@ MutableDomItem DomItem::makeCopy(DomItem::CopyOption option) return MutableDomItem(newItem.path(pathFromOwner())); } -bool DomItem::commitToBase(std::shared_ptr<DomEnvironment> validEnvPtr) +bool DomItem::commitToBase(const std::shared_ptr<DomEnvironment> &validEnvPtr) const { DomItem env = environment(); if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) { @@ -2046,7 +2220,7 @@ bool DomItem::commitToBase(std::shared_ptr<DomEnvironment> validEnvPtr) return false; } -bool DomItem::visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)> visitor) +bool DomItem::visitLocalSymbolsNamed(const QString &name, function_ref<bool(const DomItem &)> visitor) const { if (name.isEmpty()) // no empty symbol return true; @@ -2089,7 +2263,7 @@ bool DomItem::visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)> break; case DomType::MethodInfo: { DomItem params = field(Fields::parameters); - if (!params.visitIndexes([name, visitor](DomItem &p) { + if (!params.visitIndexes([name, visitor](const DomItem &p) { const MethodParameter *pPtr = p.as<MethodParameter>(); if (pPtr->name == name && !visitor(p)) return false; @@ -2123,33 +2297,31 @@ bool DomItem::visitLocalSymbolsNamed(QString name, function_ref<bool(DomItem &)> return true; } -DomItem DomItem::operator[](const QString &cName) +DomItem DomItem::operator[](const QString &cName) const { if (internalKind() == DomType::Map) return key(cName); return field(cName); } -DomItem DomItem::operator[](QStringView cName) +DomItem DomItem::operator[](QStringView cName) const { if (internalKind() == DomType::Map) return key(cName.toString()); return field(cName); } -DomItem DomItem::operator[](Path p) +DomItem DomItem::operator[](const Path &p) const { return path(p); } -QCborValue DomItem::value() +QCborValue DomItem::value() const { - if (internalKind() == DomType::ConstantData) - return std::get<ConstantData>(m_element).value(); - return QCborValue(); + return base()->value(); } -void DomItem::dumpPtr(Sink sink) +void DomItem::dumpPtr(const Sink &sink) const { sink(u"DomItem{ topPtr:"); sink(QString::number((quintptr)topPtr().get(), 16)); @@ -2162,16 +2334,17 @@ void DomItem::dumpPtr(Sink sink) sink(u"}"); } -void DomItem::dump(Sink s, int indent, - function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) +void DomItem::dump( + const Sink &s, int indent, + function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const { visitEl([this, s, indent, filter](auto &&e) { e->dump(*this, s, indent, filter); }); } FileWriter::Status -DomItem::dump(QString path, - function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter, - int nBackups, int indent, FileWriter *fw) +DomItem::dump(const QString &path, + function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter, + int nBackups, int indent, FileWriter *fw) const { FileWriter localFw; if (!fw) @@ -2194,122 +2367,140 @@ DomItem::dump(QString path, return fw->status; } -QString DomItem::toString() +QString DomItem::toString() const { - return dumperToString([this](Sink s){ dump(s); }); + return dumperToString([this](const Sink &s){ dump(s); }); } -int DomItem::derivedFrom() +int DomItem::derivedFrom() const { - if (m_owner) - return std::visit([](auto &&ow) { return ow->derivedFrom(); }, *m_owner); - return 0; + return std::visit([](auto &&ow) { + if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>) + return 0; + else + return ow->derivedFrom(); + }, m_owner); } -int DomItem::revision() +int DomItem::revision() const { - if (m_owner) - return std::visit([](auto &&ow) { return ow->revision(); }, *m_owner); - else - return -1; + return std::visit([](auto &&ow) { + if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>) + return -1; + else + return ow->revision(); + }, m_owner); } -QDateTime DomItem::createdAt() +QDateTime DomItem::createdAt() const { - if (m_owner) - return std::visit([](auto &&ow) { return ow->createdAt(); }, *m_owner); - else - return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC); + return std::visit([](auto &&ow) { + if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>) + return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC); + else + return ow->createdAt(); + }, m_owner); } -QDateTime DomItem::frozenAt() +QDateTime DomItem::frozenAt() const { - if (m_owner) - return std::visit([](auto &&ow) { return ow->frozenAt(); }, *m_owner); - else - return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC); + return std::visit([](auto &&ow) { + if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>) + return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC); + else + return ow->frozenAt(); + }, m_owner); } -QDateTime DomItem::lastDataUpdateAt() +QDateTime DomItem::lastDataUpdateAt() const { - if (m_owner) - return std::visit([](auto &&ow) { return ow->lastDataUpdateAt(); }, *m_owner); - else - return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC); + return std::visit([](auto &&ow) { + if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>) + return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC); + else + return ow->lastDataUpdateAt(); + }, m_owner); } -void DomItem::addError(ErrorMessage msg) +void DomItem::addError(ErrorMessage &&msg) const { - if (m_owner) { - DomItem myOwner = owner(); - std::visit( - [this, &myOwner, &msg](auto &&ow) { ow->addError(myOwner, msg.withItem(*this)); }, - *m_owner); - } else - defaultErrorHandler(msg.withItem(*this)); + std::visit([this, &msg](auto &&ow) { + if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>) + defaultErrorHandler(msg.withItem(*this)); + else + ow->addError(owner(), std::move(msg.withItem(*this))); + }, m_owner); } -ErrorHandler DomItem::errorHandler() +ErrorHandler DomItem::errorHandler() const { - DomItem self = *this; - return [self](ErrorMessage m) mutable { self.addError(m); }; + // We need a copy here. Error handlers may be called when this is gone. + return [self = *this](const ErrorMessage &m) { self.addError(ErrorMessage(m)); }; } -void DomItem::clearErrors(ErrorGroups groups, bool iterate) +void DomItem::clearErrors(const ErrorGroups &groups, bool iterate) const { - if (m_owner) { - std::visit([&groups](auto &&ow) { ow->clearErrors(groups); }, *m_owner); - if (iterate) - iterateSubOwners([groups](DomItem i){ - i.clearErrors(groups, true); - return true; - }); + std::visit([&groups](auto &&ow) { + if constexpr (!std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>) + ow->clearErrors(groups); + }, m_owner); + + if (iterate) { + iterateSubOwners([groups](const DomItem &i){ + i.clearErrors(groups, true); + return true; + }); } } -bool DomItem::iterateErrors(function_ref<bool(DomItem, ErrorMessage)> visitor, bool iterate, - Path inPath) +bool DomItem::iterateErrors( + function_ref<bool(const DomItem &, const ErrorMessage &)> visitor, bool iterate, + Path inPath) const { - if (m_owner) { - DomItem ow = owner(); - if (!std::visit([&ow, visitor, - inPath](auto &&el) { return el->iterateErrors(ow, visitor, inPath); }, - *m_owner)) - return false; - if (iterate && !iterateSubOwners([inPath, visitor](DomItem &i) { - return i.iterateErrors(visitor, true, inPath); - })) - return false; + if (!std::visit([this, visitor, inPath](auto &&el) { + if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) + return true; + else + return el->iterateErrors(owner(), visitor, inPath); + }, m_owner)) { + return false; + } + + if (iterate && !iterateSubOwners([inPath, visitor](const DomItem &i) { + return i.iterateErrors(visitor, true, inPath); + })) { + return false; } + return true; } -bool DomItem::iterateSubOwners(function_ref<bool(DomItem &)> visitor) +bool DomItem::iterateSubOwners(function_ref<bool(const DomItem &)> visitor) const { - if (m_owner) { - DomItem ow = owner(); - return std::visit([&ow, visitor](auto &&o) { return o->iterateSubOwners(ow, visitor); }, - *m_owner); - } - return true; + return std::visit([this, visitor](auto &&o) { + if constexpr (std::is_same_v<std::decay_t<decltype(o)>, std::monostate>) + return true; + else + return o->iterateSubOwners(owner(), visitor); + }, m_owner); } -bool DomItem::iterateDirectSubpaths(DirectVisitor v) +bool DomItem::iterateDirectSubpaths(DirectVisitor v) const { - return visitMutableEl( - [this, v](auto &&el) mutable { return el->iterateDirectSubpaths(*this, v); }); + return visitEl( + [this, v](auto &&el) { return el->iterateDirectSubpaths(*this, v); }); } -DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, QList<Path> paths) +DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, const QList<Path> &paths) const { return subListItem( List::fromQList<Path>(pathFromOwner().appendComponent(c), paths, - [](DomItem &list, const PathEls::PathComponent &p, Path &el) { + [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) { return list.subReferenceItem(p, el); })); } -DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, Path referencedObject) +DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const { if (domTypeIsOwningItem(internalKind())) { return DomItem(m_top, m_owner, m_ownerPath, Reference(referencedObject, Path(c))); @@ -2319,126 +2510,45 @@ DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, Path referenc } } -shared_ptr<DomTop> DomItem::topPtr() -{ - if (m_top) - return std::visit([](auto &&el) -> shared_ptr<DomTop> { return el; }, *m_top); - return {}; -} - -shared_ptr<OwningItem> DomItem::owningItemPtr() +shared_ptr<DomTop> DomItem::topPtr() const { - if (m_owner) - return std::visit([](auto &&el) -> shared_ptr<OwningItem> { return el; }, *m_owner); - return {}; + return std::visit([](auto &&el) -> shared_ptr<DomTop> { + if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) + return {}; + else + return el; + }, m_top); } -const DomBase *DomItem::base() +shared_ptr<OwningItem> DomItem::owningItemPtr() const { - return visitEl([](auto &&el) { return static_cast<const DomBase *>(&(*el)); }); + return std::visit([](auto &&el) -> shared_ptr<OwningItem> { + if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) + return {}; + else + return el; + }, m_owner); } -DomBase *DomItem::mutableBase() +/*! + \internal + Returns a pointer to the virtual base pointer to a DomBase. +*/ +const DomBase *DomItem::base() const { - return visitMutableEl([](auto &&el) { return static_cast<DomBase *>(&(*el)); }); + return visitEl([](auto &&el) -> const DomBase * { return el->domBase(); }); } -DomItem::DomItem(std::shared_ptr<DomEnvironment> envPtr): +DomItem::DomItem(const std::shared_ptr<DomEnvironment> &envPtr): DomItem(envPtr, envPtr, Path(), envPtr.get()) { } -DomItem::DomItem(std::shared_ptr<DomUniverse> universePtr): +DomItem::DomItem(const std::shared_ptr<DomUniverse> &universePtr): DomItem(universePtr, universePtr, Path(), universePtr.get()) { } -void DomItem::loadFile(QString canonicalFilePath, QString logicalPath, QString code, - QDateTime codeDate, DomTop::Callback callback, LoadOptions loadOptions, - std::optional<DomType> fileType) -{ - DomItem topEl = top(); - if (topEl.internalKind() == DomType::DomEnvironment - || topEl.internalKind() == DomType::DomUniverse) { - if (auto univ = topEl.ownerAs<DomUniverse>()) - univ->loadFile(*this, canonicalFilePath, logicalPath, code, codeDate, callback, - loadOptions, fileType); - else if (auto env = topEl.ownerAs<DomEnvironment>()) { - if (env->options() & DomEnvironment::Option::NoDependencies) - env->loadFile(topEl, canonicalFilePath, logicalPath, code, codeDate, callback, - DomTop::Callback(), DomTop::Callback(), loadOptions, fileType); - else - env->loadFile(topEl, canonicalFilePath, logicalPath, code, codeDate, - DomTop::Callback(), DomTop::Callback(), callback, loadOptions, - fileType); - } else - Q_ASSERT(false && "expected either DomUniverse or DomEnvironment cast to succeed"); - } else { - addError(myErrors().warning(tr("loadFile called without DomEnvironment or DomUniverse."))); - callback(Paths::qmlFileInfoPath(canonicalFilePath), DomItem::empty, DomItem::empty); - } -} - -void DomItem::loadFile(QString filePath, QString logicalPath, DomTop::Callback callback, - LoadOptions loadOptions, std::optional<DomType> fileType) -{ - DomItem topEl = top(); - if (topEl.internalKind() == DomType::DomEnvironment - || topEl.internalKind() == DomType::DomUniverse) { - if (auto univ = topEl.ownerAs<DomUniverse>()) - univ->loadFile(*this, filePath, logicalPath, callback, loadOptions); - else if (auto env = topEl.ownerAs<DomEnvironment>()) { - if (env->options() & DomEnvironment::Option::NoDependencies) - env->loadFile(topEl, filePath, logicalPath, callback, DomTop::Callback(), - DomTop::Callback(), loadOptions, fileType); - else - env->loadFile(topEl, filePath, logicalPath, DomTop::Callback(), DomTop::Callback(), - callback, loadOptions, fileType); - } else - Q_ASSERT(false && "expected either DomUniverse or DomEnvironment cast to succeed"); - } else { - addError(myErrors().warning(tr("loadFile called without DomEnvironment or DomUniverse."))); - callback(Paths::qmlFileInfoPath(filePath), DomItem::empty, DomItem::empty); - } -} - -void DomItem::loadModuleDependency(QString uri, Version version, - std::function<void(Path, DomItem &, DomItem &)> callback, - ErrorHandler errorHandler) -{ - DomItem topEl = top(); - if (topEl.internalKind() == DomType::DomEnvironment) { - if (auto envPtr = topEl.ownerAs<DomEnvironment>()) { - if (envPtr->options() & DomEnvironment::Option::NoDependencies) - envPtr->loadModuleDependency(topEl, uri, version, callback, nullptr, errorHandler); - else - envPtr->loadModuleDependency(topEl, uri, version, nullptr, callback, errorHandler); - } else - Q_ASSERT(false && "loadDependency expected the DomEnvironment cast to succeed"); - } else { - addError(myErrors().warning(tr("loadModuleDependency called without DomEnvironment."))); - callback(Paths::moduleScopePath(uri, version), DomItem::empty, DomItem::empty); - } -} - -void DomItem::loadBuiltins(std::function<void(Path, DomItem &, DomItem &)> callback, ErrorHandler h) -{ - DomItem env = environment(); - if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) - envPtr->loadBuiltins(env, callback, h); - else - myErrors().error(tr("Cannot load builtins without DomEnvironment")).handle(h); -} - -void DomItem::loadPendingDependencies() -{ - DomItem env = environment(); - if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) - envPtr->loadPendingDependencies(env); - else - myErrors().error(tr("Called loadPendingDependencies without environment")).handle(); -} - /*! \brief Creates a new document with the given code @@ -2447,54 +2557,57 @@ The fileType should normally be QmlFile, but you might want to load a qmltypes f example and interpret it as qmltypes file (not plain Qml), or as JsFile. In those case set the file type accordingly. */ -DomItem DomItem::fromCode(QString code, DomType fileType) +DomItem DomItem::fromCode(const QString &code, DomType fileType) { if (code.isEmpty()) return DomItem(); - DomItem env = + auto env = DomEnvironment::create(QStringList(), QQmlJS::Dom::DomEnvironment::Option::SingleThreaded | QQmlJS::Dom::DomEnvironment::Option::NoDependencies); DomItem tFile; - env.loadFile( - QString(), QString(), code, QDateTime::currentDateTimeUtc(), + + env->loadFile( + FileToLoad::fromMemory(env, QString(), code), [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; }, - LoadOption::DefaultLoad, fileType); - env.loadPendingDependencies(); + std::make_optional(fileType)); + env->loadPendingDependencies(); return tFile.fileObject(); } Empty::Empty() {} -Path Empty::pathFromOwner(DomItem &) const +Path Empty::pathFromOwner(const DomItem &) const { return Path(); } -Path Empty::canonicalPath(DomItem &) const +Path Empty::canonicalPath(const DomItem &) const { return Path(); } -bool Empty::iterateDirectSubpaths(DomItem &, DirectVisitor) +bool Empty::iterateDirectSubpaths(const DomItem &, DirectVisitor) const { return true; } -DomItem Empty::containingObject(DomItem &self) const +DomItem Empty::containingObject(const DomItem &self) const { return self; } -void Empty::dump(DomItem &, Sink s, int, - function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)>) const +void Empty::dump( + const DomItem &, const Sink &s, int, + function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>) const { s(u"null"); } -Map::Map(Path pathFromOwner, Map::LookupFunction lookup, Keys keys, QString targetType) +Map::Map(const Path &pathFromOwner, const Map::LookupFunction &lookup, + const Keys &keys, const QString &targetType) : DomElement(pathFromOwner), m_lookup(lookup), m_keys(keys), m_targetType(targetType) {} @@ -2503,31 +2616,31 @@ quintptr Map::id() const return quintptr(0); } -bool Map::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) +bool Map::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const { QSet<QString> ksSet = keys(self); QStringList ksList = QStringList(ksSet.begin(), ksSet.end()); std::sort(ksList.begin(), ksList.end()); - for (QString k : ksList) { + for (const QString &k : std::as_const(ksList)) { if (!visitor(PathEls::Key(k), [&self, this, k]() { return key(self, k); })) return false; } return true; } -const QSet<QString> Map::keys(DomItem &self) const +const QSet<QString> Map::keys(const DomItem &self) const { return m_keys(self); } -DomItem Map::key(DomItem &self, QString name) const +DomItem Map::key(const DomItem &self, const QString &name) const { return m_lookup(self, name); } void DomBase::dump( - DomItem &self, Sink sink, int indent, - function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) const + const DomItem &self, const Sink &sink, int indent, + function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const { bool comma = false; DomKind dK = self.domKind(); @@ -2566,6 +2679,9 @@ void DomBase::dump( case DomKind::Map: sink(u"{"); break; + case DomKind::ScriptElement: + // nothing to print + break; } auto closeParens = qScopeGuard( [dK, sink, indent]{ @@ -2586,6 +2702,9 @@ void DomBase::dump( sinkNewline(sink, indent); sink(u"}"); break; + case DomKind::ScriptElement: + // nothing to print + break; } }); index_type idx = 0; @@ -2639,8 +2758,9 @@ void DomBase::dump( }); } -List::List(Path pathFromOwner, List::LookupFunction lookup, List::Length length, - List::IteratorFunction iterator, QString elType): +List::List(const Path &pathFromOwner, const List::LookupFunction &lookup, + const List::Length &length, const List::IteratorFunction &iterator, + const QString &elType): DomElement(pathFromOwner), m_lookup(lookup), m_length(length), m_iterator(iterator), m_elType(elType) {} @@ -2651,12 +2771,12 @@ quintptr List::id() const } void List::dump( - DomItem &self, Sink sink, int indent, - function_ref<bool(DomItem &, const PathEls::PathComponent &, DomItem &)> filter) const + const DomItem &self, const Sink &sink, int indent, + function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const { bool first = true; sink(u"["); - const_cast<List *>(this)->iterateDirectSubpaths( + iterateDirectSubpaths( self, [&self, indent, &first, sink, filter](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) { @@ -2674,7 +2794,7 @@ void List::dump( sink(u"]"); } -bool List::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) +bool List::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const { if (m_iterator) { return m_iterator(self, [visitor](index_type i, function_ref<DomItem()> itemF) { @@ -2689,22 +2809,22 @@ bool List::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) return true; } -index_type List::indexes(DomItem &self) const +index_type List::indexes(const DomItem &self) const { return m_length(self); } -DomItem List::index(DomItem &self, index_type index) const +DomItem List::index(const DomItem &self, index_type index) const { return m_lookup(self, index); } -void List::writeOut(DomItem &self, OutWriter &ow, bool compact) const +void List::writeOut(const DomItem &self, OutWriter &ow, bool compact) const { - ow.writeRegion(u"leftSquareBrace", u"["); + ow.writeRegion(LeftBracketRegion); int baseIndent = ow.increaseIndent(1); bool first = true; - const_cast<List *>(this)->iterateDirectSubpaths( + iterateDirectSubpaths( self, [&ow, &first, compact](const PathEls::PathComponent &, function_ref<DomItem()> elF) { if (first) @@ -2720,30 +2840,30 @@ void List::writeOut(DomItem &self, OutWriter &ow, bool compact) const if (!compact && !first) ow.newline(); ow.decreaseIndent(1, baseIndent); - ow.writeRegion(u"rightSquareBrace", u"]"); + ow.writeRegion(RightBracketRegion); } -DomElement::DomElement(Path pathFromOwner) : m_pathFromOwner(pathFromOwner) { } +DomElement::DomElement(const Path &pathFromOwner) : m_pathFromOwner(pathFromOwner) { } -Path DomElement::pathFromOwner(DomItem &) const +Path DomElement::pathFromOwner(const DomItem &) const { Q_ASSERT(m_pathFromOwner && "uninitialized DomElement"); return m_pathFromOwner; } -Path DomElement::canonicalPath(DomItem &self) const +Path DomElement::canonicalPath(const DomItem &self) const { Q_ASSERT(m_pathFromOwner && "uninitialized DomElement"); return self.owner().canonicalPath().path(m_pathFromOwner); } -DomItem DomElement::containingObject(DomItem &self) const +DomItem DomElement::containingObject(const DomItem &self) const { Q_ASSERT(m_pathFromOwner && "uninitialized DomElement"); return DomBase::containingObject(self); } -void DomElement::updatePathFromOwner(Path newPath) +void DomElement::updatePathFromOwner(const Path &newPath) { //if (!domTypeCanBeInline(kind())) m_pathFromOwner = newPath; @@ -2751,7 +2871,7 @@ void DomElement::updatePathFromOwner(Path newPath) bool Reference::shouldCache() const { - for (Path p : referredObjectPath) { + for (const Path &p : referredObjectPath) { switch (p.headKind()) { case Path::Kind::Current: switch (p.headCurrent()) { @@ -2776,7 +2896,7 @@ bool Reference::shouldCache() const return false; } -Reference::Reference(Path referredObject, Path pathFromOwner, const SourceLocation &) +Reference::Reference(const Path &referredObject, const Path &pathFromOwner, const SourceLocation &) : DomElement(pathFromOwner), referredObjectPath(referredObject) { } @@ -2786,7 +2906,7 @@ quintptr Reference::id() const return quintptr(0); } -bool Reference::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) +bool Reference::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const { bool cont = true; cont = cont && self.dvValueLazyField(visitor, Fields::referredObjectPath, [this]() { @@ -2797,7 +2917,7 @@ bool Reference::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) return cont; } -DomItem Reference::field(DomItem &self, QStringView name) const +DomItem Reference::field(const DomItem &self, QStringView name) const { if (Fields::referredObjectPath == name) return self.subDataItemField(Fields::referredObjectPath, referredObjectPath.toString()); @@ -2806,22 +2926,22 @@ DomItem Reference::field(DomItem &self, QStringView name) const return DomItem(); } -QList<QString> Reference::fields(DomItem &) const +QList<QString> Reference::fields(const DomItem &) const { return QList<QString>({QString::fromUtf16(Fields::referredObjectPath), QString::fromUtf16(Fields::get)}); } -DomItem Reference::index(DomItem &, index_type) const +DomItem Reference::index(const DomItem &, index_type) const { return DomItem(); } -DomItem Reference::key(DomItem &, QString) const +DomItem Reference::key(const DomItem &, const QString &) const { return DomItem(); } -DomItem Reference::get(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs) const +DomItem Reference::get(const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const { DomItem res; if (referredObjectPath) { @@ -2857,7 +2977,7 @@ DomItem Reference::get(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs) QList<Path> visitedRefsLocal; self.resolve( referredObjectPath, - [&res](Path, DomItem &el) { + [&res](Path, const DomItem &el) { res = el; return false; }, @@ -2870,7 +2990,8 @@ DomItem Reference::get(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs) return res; } -QList<DomItem> Reference::getAll(DomItem &self, ErrorHandler h, QList<Path> *visitedRefs) const +QList<DomItem> Reference::getAll( + const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const { QList<DomItem> res; if (referredObjectPath) { @@ -2893,7 +3014,7 @@ QList<DomItem> Reference::getAll(DomItem &self, ErrorHandler h, QList<Path> *vis } if (!cachedPaths.isEmpty()) { bool outdated = false; - for (Path p : cachedPaths) { + for (const Path &p : cachedPaths) { DomItem newEl = env.path(p); if (!newEl) { outdated = true; @@ -2912,22 +3033,23 @@ QList<DomItem> Reference::getAll(DomItem &self, ErrorHandler h, QList<Path> *vis } self.resolve( referredObjectPath, - [&res](Path, DomItem &el) { + [&res](Path, const DomItem &el) { res.append(el); return true; }, h, ResolveOption::None, referredObjectPath, visitedRefs); if (env) { QList<Path> canonicalPaths; - for (DomItem i : res) { + for (const DomItem &i : res) { if (i) canonicalPaths.append(i.canonicalPath()); else qCWarning(refLog) << "getAll of reference at " << selfPath << " visits empty items."; } - RefCacheEntry::addForPath(env, selfPath, - RefCacheEntry { RefCacheEntry::Cached::All, canonicalPaths }); + RefCacheEntry::addForPath( + env, selfPath, + RefCacheEntry { RefCacheEntry::Cached::All, std::move(canonicalPaths) }); } } return res; @@ -2958,7 +3080,7 @@ OwningItem::OwningItem(int derivedFrom) m_frozenAt(QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)) {} -OwningItem::OwningItem(int derivedFrom, QDateTime lastDataUpdateAt) +OwningItem::OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt) : m_derivedFrom(derivedFrom), m_revision(nextRevision()), m_createdAt(QDateTime::currentDateTimeUtc()), @@ -2992,14 +3114,14 @@ int OwningItem::nextRevision() return ++nextRev; } -bool OwningItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) +bool OwningItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const { bool cont = true; cont = cont && self.dvItemField(visitor, Fields::errors, [&self, this]() { QMultiMap<Path, ErrorMessage> myErrors = localErrors(); return self.subMapItem(Map( self.pathFromOwner().field(Fields::errors), - [myErrors](DomItem &map, QString key) { + [myErrors](const DomItem &map, const QString &key) { auto it = myErrors.find(Path::fromString(key)); if (it != myErrors.end()) return map.subDataItem(PathEls::Key(key), it->toCbor(), @@ -3007,7 +3129,7 @@ bool OwningItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) else return DomItem(); }, - [myErrors](DomItem &) { + [myErrors](const DomItem &) { QSet<QString> res; auto it = myErrors.keyBegin(); auto end = myErrors.keyEnd(); @@ -3020,7 +3142,7 @@ bool OwningItem::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) return cont; } -DomItem OwningItem::containingObject(DomItem &self) const +DomItem OwningItem::containingObject(const DomItem &self) const { Source s = self.canonicalPath().split(); if (s.pathFromSource) { @@ -3078,12 +3200,12 @@ void OwningItem::refreshedDataAt(QDateTime tNew) m_lastDataUpdateAt = tNew; } -void OwningItem::addError(DomItem &, ErrorMessage msg) +void OwningItem::addError(const DomItem &, ErrorMessage &&msg) { - addErrorLocal(msg); + addErrorLocal(std::move(msg)); } -void OwningItem::addErrorLocal(ErrorMessage msg) +void OwningItem::addErrorLocal(ErrorMessage &&msg) { QMutexLocker l(mutex()); quint32 &c = m_errorsCounts[msg]; @@ -3092,7 +3214,7 @@ void OwningItem::addErrorLocal(ErrorMessage msg) m_errors.insert(msg.path, msg); } -void OwningItem::clearErrors(ErrorGroups groups) +void OwningItem::clearErrors(const ErrorGroups &groups) { QMutexLocker l(mutex()); auto it = m_errors.begin(); @@ -3104,8 +3226,9 @@ void OwningItem::clearErrors(ErrorGroups groups) } } -bool OwningItem::iterateErrors(DomItem &self, function_ref<bool(DomItem, ErrorMessage)> visitor, - Path inPath) +bool OwningItem::iterateErrors( + const DomItem &self, function_ref<bool(const DomItem &, const ErrorMessage &)> visitor, + const Path &inPath) { QMultiMap<Path, ErrorMessage> myErrors; { @@ -3121,7 +3244,7 @@ bool OwningItem::iterateErrors(DomItem &self, function_ref<bool(DomItem, ErrorMe return true; } -bool OwningItem::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owner)> visitor) +bool OwningItem::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor) { return self.iterateDirectSubpaths( [&self, visitor](const PathEls::PathComponent &, function_ref<DomItem()> iF) { @@ -3135,13 +3258,11 @@ bool OwningItem::iterateSubOwners(DomItem &self, function_ref<bool(DomItem &owne }); } -bool operator==(const DomItem &o1c, const DomItem &o2c) +bool operator==(const DomItem &o1, const DomItem &o2) { - DomItem &o1 = *const_cast<DomItem *>(&o1c); - DomItem &o2 = *const_cast<DomItem *>(&o2c); if (o1.m_kind != o2.m_kind) return false; - return o1.visitMutableEl([&o1, &o2](auto &&el1) { + return o1.visitEl([&o1, &o2](auto &&el1) { auto &&el2 = std::get<std::decay_t<decltype(el1)>>(o2.m_element); auto id1 = el1->id(); auto id2 = el2->id(); @@ -3162,10 +3283,10 @@ bool operator==(const DomItem &o1c, const DomItem &o2c) ErrorHandler MutableDomItem::errorHandler() { MutableDomItem self; - return [&self](ErrorMessage m) { self.addError(m); }; + return [&self](const ErrorMessage &m) { self.addError(ErrorMessage(m)); }; } -MutableDomItem MutableDomItem::addPrototypePath(Path prototypePath) +MutableDomItem MutableDomItem::addPrototypePath(const Path &prototypePath) { if (QmlObject *el = mutableAs<QmlObject>()) { return path(el->addPrototypePath(prototypePath)); @@ -3175,7 +3296,7 @@ MutableDomItem MutableDomItem::addPrototypePath(Path prototypePath) } } -MutableDomItem MutableDomItem::setNextScopePath(Path nextScopePath) +MutableDomItem MutableDomItem::setNextScopePath(const Path &nextScopePath) { if (QmlObject *el = mutableAs<QmlObject>()) { el->setNextScopePath(nextScopePath); @@ -3217,7 +3338,7 @@ MutableDomItem MutableDomItem::setMethods(QMultiMap<QString, MethodInfo> functio return {}; } -MutableDomItem MutableDomItem::setChildren(QList<QmlObject> children) +MutableDomItem MutableDomItem::setChildren(const QList<QmlObject> &children) { if (QmlObject *el = mutableAs<QmlObject>()) { el->setChildren(children); @@ -3227,7 +3348,7 @@ MutableDomItem MutableDomItem::setChildren(QList<QmlObject> children) return {}; } -MutableDomItem MutableDomItem::setAnnotations(QList<QmlObject> annotations) +MutableDomItem MutableDomItem::setAnnotations(const QList<QmlObject> &annotations) { if (QmlObject *el = mutableAs<QmlObject>()) el->setAnnotations(annotations); @@ -3248,7 +3369,7 @@ MutableDomItem MutableDomItem::setAnnotations(QList<QmlObject> annotations) } return field(Fields::annotations); } -MutableDomItem MutableDomItem::setScript(std::shared_ptr<ScriptExpression> exp) +MutableDomItem MutableDomItem::setScript(const std::shared_ptr<ScriptExpression> &exp) { switch (internalKind()) { case DomType::Binding: @@ -3265,8 +3386,14 @@ MutableDomItem MutableDomItem::setScript(std::shared_ptr<ScriptExpression> exp) break; case DomType::MethodParameter: if (MethodParameter *p = mutableAs<MethodParameter>()) { - p->defaultValue = exp; - return field(Fields::body); + if (exp->expressionType() == ScriptExpression::ExpressionType::ArgInitializer) { + p->defaultValue = exp; + return field(Fields::defaultValue); + } + if (exp->expressionType() == ScriptExpression::ExpressionType::ArgumentStructure) { + p->value = exp; + return field(Fields::value); + } } break; case DomType::ScriptExpression: @@ -3280,7 +3407,7 @@ MutableDomItem MutableDomItem::setScript(std::shared_ptr<ScriptExpression> exp) return MutableDomItem(); } -MutableDomItem MutableDomItem::setCode(QString code) +MutableDomItem MutableDomItem::setCode(const QString &code) { DomItem it = item(); switch (it.internalKind()) { @@ -3323,7 +3450,8 @@ MutableDomItem MutableDomItem::setCode(QString code) return MutableDomItem(); } -MutableDomItem MutableDomItem::addPropertyDef(PropertyDefinition propertyDef, AddOption option) +MutableDomItem MutableDomItem::addPropertyDef( + const PropertyDefinition &propertyDef, AddOption option) { if (QmlObject *el = mutableAs<QmlObject>()) return el->addPropertyDef(*this, propertyDef, option); @@ -3341,7 +3469,7 @@ MutableDomItem MutableDomItem::addBinding(Binding binding, AddOption option) return MutableDomItem(); } -MutableDomItem MutableDomItem::addMethod(MethodInfo functionDef, AddOption option) +MutableDomItem MutableDomItem::addMethod(const MethodInfo &functionDef, AddOption option) { if (QmlObject *el = mutableAs<QmlObject>()) return el->addMethod(*this, functionDef, option); @@ -3394,17 +3522,17 @@ MutableDomItem MutableDomItem::addAnnotation(QmlObject annotation) return MutableDomItem(owner().item(), res); } -MutableDomItem MutableDomItem::addPreComment(const Comment &comment, QString regionName) +MutableDomItem MutableDomItem::addPreComment(const Comment &comment, FileLocationRegion region) { index_type idx; MutableDomItem rC = field(Fields::comments); if (auto rcPtr = rC.mutableAs<RegionComments>()) { - auto &preList = rcPtr->regionComments[regionName].preComments; - idx = preList.size(); - preList.append(comment); + auto commentedElement = rcPtr->regionComments()[region]; + idx = commentedElement.preComments().size(); + commentedElement.addComment(comment); MutableDomItem res = path(Path::Field(Fields::comments) .field(Fields::regionComments) - .key(regionName) + .key(fileLocationRegionName(region)) .field(Fields::preComments) .index(idx)); Q_ASSERT(res); @@ -3413,17 +3541,17 @@ MutableDomItem MutableDomItem::addPreComment(const Comment &comment, QString reg return MutableDomItem(); } -MutableDomItem MutableDomItem::addPostComment(const Comment &comment, QString regionName) +MutableDomItem MutableDomItem::addPostComment(const Comment &comment, FileLocationRegion region) { index_type idx; MutableDomItem rC = field(Fields::comments); if (auto rcPtr = rC.mutableAs<RegionComments>()) { - auto &postList = rcPtr->regionComments[regionName].postComments; - idx = postList.size(); - postList.append(comment); + auto commentedElement = rcPtr->regionComments()[region]; + idx = commentedElement.postComments().size(); + commentedElement.addComment(comment); MutableDomItem res = path(Path::Field(Fields::comments) .field(Fields::regionComments) - .key(regionName) + .key(fileLocationRegionName(region)) .field(Fields::postComments) .index(idx)); Q_ASSERT(res); @@ -3434,7 +3562,7 @@ MutableDomItem MutableDomItem::addPostComment(const Comment &comment, QString re QDebug operator<<(QDebug debug, const DomItem &c) { - dumperToQDebug([&c](Sink s) { const_cast<DomItem *>(&c)->dump(s); }, debug); + dumperToQDebug([&c](const Sink &s) { c.dump(s); }, debug); return debug; } @@ -3445,7 +3573,7 @@ QDebug operator<<(QDebug debug, const MutableDomItem &c) << ", " << cc.canonicalPath().toString() << ")"; } -bool ListPBase::iterateDirectSubpaths(DomItem &self, DirectVisitor v) +bool ListPBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const { index_type len = index_type(m_pList.size()); for (index_type i = 0; i < len; ++i) { @@ -3455,9 +3583,9 @@ bool ListPBase::iterateDirectSubpaths(DomItem &self, DirectVisitor v) return true; } -void ListPBase::writeOut(DomItem &self, OutWriter &ow, bool compact) const +void ListPBase::writeOut(const DomItem &self, OutWriter &ow, bool compact) const { - ow.writeRegion(u"leftSquareBrace", u"["); + ow.writeRegion(LeftBracketRegion); int baseIndent = ow.increaseIndent(1); bool first = true; index_type len = index_type(m_pList.size()); @@ -3474,7 +3602,36 @@ void ListPBase::writeOut(DomItem &self, OutWriter &ow, bool compact) const if (!compact && !first) ow.newline(); ow.decreaseIndent(1, baseIndent); - ow.writeRegion(u"rightSquareBrace", u"]"); + ow.writeRegion(RightBracketRegion); +} + +QQmlJSScope::ConstPtr ScriptElement::semanticScope() +{ + return m_scope; +} +void ScriptElement::setSemanticScope(const QQmlJSScope::ConstPtr &scope) +{ + m_scope = scope; +} + +/*! + \internal + \brief Returns a pointer to the virtual base for virtual method calls. + + A helper to call virtual methods without having to call std::visit(...). + */ +ScriptElement::PointerType<ScriptElement> ScriptElementVariant::base() const +{ + if (!m_data) + return nullptr; + + return std::visit( + [](auto &&e) { + // std::reinterpret_pointer_cast does not exist on qnx it seems... + return std::shared_ptr<ScriptElement>( + e, static_cast<ScriptElement *>(e.get())); + }, + *m_data); } } // end namespace Dom |