Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldomitem.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
7#include "qqmldomtop_p.h"
10#include "qqmldommock_p.h"
20#include <qqmldomlinewriterfactory_p.h>
21
22#include <QtQml/private/qqmljslexer_p.h>
23#include <QtQml/private/qqmljsparser_p.h>
24#include <QtQml/private/qqmljsengine_p.h>
25#include <QtQml/private/qqmljsastvisitor_p.h>
26#include <QtQml/private/qqmljsast_p.h>
27
28#include <QtCore/QCborArray>
29#include <QtCore/QCborMap>
30#include <QtCore/QDebug>
31#include <QtCore/QDir>
32#include <QtCore/QFile>
33#include <QtCore/QFileInfo>
34#include <QtCore/QJsonDocument>
35#include <QtCore/QJsonValue>
36#include <QtCore/QMutexLocker>
37#include <QtCore/QRegularExpression>
38#include <QtCore/QScopeGuard>
39#include <QtCore/QtGlobal>
40#include <QtCore/QTimeZone>
41#include <optional>
42#include <type_traits>
43#include <utility>
44
46
47Q_LOGGING_CATEGORY(writeOutLog, "qt.qmldom.writeOut", QtWarningMsg);
48Q_STATIC_LOGGING_CATEGORY(refLog, "qt.qmldom.ref", QtWarningMsg);
49
50namespace QQmlJS {
51namespace Dom {
52
53template<class... TypeList>
55
56template<class... Ts>
57struct CheckDomElementT<std::variant<Ts...>> : std::conjunction<IsInlineDom<Ts>...>
58{
59};
60
61/*!
62 \internal
63 \class QQmljs::Dom::ElementT
64
65 \brief A variant that contains all the Dom elements that an DomItem can contain.
66
67 Types in this variant are divided in two categories: normal Dom elements and internal Dom
68 elements.
69 The first ones are inheriting directly or indirectly from DomBase, and are the usual elements
70 that a DomItem can wrap around, like a QmlFile or an QmlObject. They should all appear in
71 ElementT as pointers, e.g. QmlFile*.
72 The internal Dom elements are a little bit special. They appear in ElementT without pointer, do
73 not inherit from DomBase \b{but} should behave like a smart DomBase-pointer. That is, they should
74 dereference as if they were a DomBase* pointing to a normal DomElement by implementing
75 operator->() and operator*().
76 Adding types here that are neither inheriting from DomBase nor implementing a smartpointer to
77 DomBase will throw compilation errors in the std::visit()-calls on this type.
78*/
79static_assert(CheckDomElementT<ElementT>::value,
80 "Types in ElementT must either be a pointer to a class inheriting "
81 "from DomBase or (for internal Dom structures) implement a smart "
82 "pointer pointing to a class inheriting from DomBase");
83
84using std::shared_ptr;
85/*!
86\internal
87\class QQmljs::Dom::DomBase
88
89\brief Abstract class common to all elements of the Qml code model
90
91DomBase represents the base class common to all objects exposed by the Dom Model
92through a DomItem.
93Single inheritence is mandatory for the inline items (Empty, Map, List, ConstantData, Reference),
94so that the base pointer of the object can be used a base pointer of the superclass directly.
95
96The subclass *must* have a
97\code
98 constexpr static Kind kindValue
99\endcode
100entry with its kind to enable casting usng the DomItem::as DomItem::ownerAs templates.
101
102The minimal overload set to be usable consists of following methods:
103\list
104\li \c{kind()} returns the kind of the current element:
105\code
106 Kind kind() const override { return kindValue; }
107\endcode
108
109\li \c{pathFromOwner()} returns the path from the owner to the current element
110\code
111 Path pathFromOwner() const override;
112\endcode
113
114\li \c{canonicalPath()} returns the path
115\code
116 Path canonicalPath(const DomItem &self) const override;
117\endcode
118
119\li \c{iterateDirectSubpaths} iterates the *direct* subpaths/children and returns false if a quick
120end was requested:
121\code
122bool iterateDirectSubpaths(const DomItem &self, function_ref<bool(Path, DomItem)>) const = 0;
123\endcode
124
125\endlist
126
127But you probably want to subclass either \c DomElement or \c OwningItem for your element. \c
128DomElement stores its \c pathFromOwner, and computes the \c canonicalPath from it and its owner. \c
129OwningItem is the unit for updates to the Dom model, exposed changes always change at least one \c
130OwningItem. They have their lifetime handled with \c shared_ptr and own (i.e. are responsible of
131freeing) other items in them.
132
133\sa QQml::Dom::DomItem, QQml::Dom::DomElement, QQml::Dom::OwningItem
134*/
135
137{
138 static QMap<DomType,QString> map = [](){
139 QMetaEnum metaEnum = QMetaEnum::fromType<DomType>();
140 QMap<DomType,QString> res;
141 for (int i = 0; i < metaEnum.keyCount(); ++ i) {
142 res[DomType(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
143 }
144 return res;
145 }();
146 return map;
147}
148
150{
151 QString res = domTypeToStringMap().value(k);
152 if (res.isEmpty())
153 return QString::number(int(k));
154 else
155 return res;
156}
157
159{
160 static QMap<DomKind, QString> map = []() {
161 QMetaEnum metaEnum = QMetaEnum::fromType<DomKind>();
162 QMap<DomKind, QString> res;
163 for (int i = 0; i < metaEnum.keyCount(); ++i) {
164 res[DomKind(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
165 }
166 return res;
167 }();
168 return map;
169}
170
172{
173 return domKindToStringMap().value(k, QString::number(int(k)));
174}
175
177{
178 switch (k) {
180 case DomType::JsFile:
181 case DomType::QmlFile:
184 return true;
185 default:
186 return false;
187 }
188}
189
191{
192 switch (k) {
195 return true;
196 default:
197 return false;
198 }
199
200}
201
203{
204 switch (k) {
205 case DomType::Map:
206 case DomType::List:
207 case DomType::ListP:
208 return true;
209 default:
210 return false;
211 }
212}
213
215{
216 switch (k) {
217 case DomType::QmlObject: // prop, methods,...
218 case DomType::ScriptExpression: // Js lexical scope
219 case DomType::QmlComponent: // (ids, enums -> qmlObj)
220 case DomType::QmlFile: // (components ->importScope)
221 case DomType::MethodInfo: // method arguments
222 case DomType::ImportScope: // (types, qualifiedImports)
223 case DomType::GlobalComponent: // global scope (enums -> qmlObj)
224 case DomType::JsResource: // js resurce (enums -> qmlObj)
225 case DomType::QmltypesComponent: // qmltypes component (enums -> qmlObj)
226 return true;
227 default:
228 return false;
229 }
230}
231
233{
234 auto parent = containingObject(self);
235 if (parent)
236 return parent.canonicalFilePath();
237 return QString();
238}
239
240void DomBase::writeOut(const DomItem &self, OutWriter &) const
241{
242 qCWarning(writeOutLog) << "Ignoring unsupported writeOut for " << domTypeToString(kind()) << ":"
243 << self.canonicalPath();
244}
245
246ConstantData::ConstantData(const Path &pathFromOwner, const QCborValue &value, Options options)
247 : DomElement(pathFromOwner), m_value(value), m_options(options)
248{}
249
250bool ConstantData::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
251{
252 static QHash<QString, QString> knownFields;
253 static QBasicMutex m;
254 auto toField = [](const QString &f) -> QStringView {
255 QMutexLocker l(&m);
256 if (!knownFields.contains(f))
257 knownFields[f] = f;
258 return knownFields[f];
259 };
260 if (m_value.isMap()) {
261 QCborMap map = m_value.toMap();
262 auto it = map.cbegin();
263 auto end = map.cend();
264 while (it != end) {
265 QString key = it.key().toString();
267 switch (m_options) {
268 case ConstantData::Options::MapIsMap:
269 comp = PathEls::Key(key);
270 break;
271 case ConstantData::Options::FirstMapIsFields:
272 comp = PathEls::Field(toField(key));
273 break;
274 }
275 auto val = it.value();
276 if (!self.invokeVisitorOnValue(visitor, comp, val))
277 return false;
278 ++it;
279 }
280 return true;
281 } else if (m_value.isArray()){
282 QCborArray array = m_value.toArray();
283 auto it = array.cbegin();
284 auto end = array.cend();
285 index_type i = 0;
286 while (it != end) {
287 if (!self.invokeVisitorOnValue(visitor, PathEls::Index(i++), *it++))
288 return false;
289 }
290 return true;
291 } else {
292 return true;
293 }
294}
295
296quintptr ConstantData::id() const
297{
298 return quintptr(0);
299}
300
301DomKind ConstantData::domKind() const
302{
303 if (m_value.isMap()) {
304 switch (m_options) {
305 case ConstantData::Options::MapIsMap:
306 return DomKind::Map;
307 case ConstantData::Options::FirstMapIsFields:
308 return DomKind::Object;
309 }
310 }
311 if (m_value.isArray())
312 return DomKind::List;
313 return DomKind::Value;
314}
315/*!
316\internal
317\class QQmlJS::Dom::DomItem
318
319\brief A value type that references any element of the Dom.
320
321This class is the central element in the Dom, it is how any element can be identfied in a uniform
322way, and provides the API to explore the Dom, and Path based operations.
323
324The DomItem (unless it is Empty) keeps a pointer to the element, and a shared pointer to its owner
325and to the DomEnvironment or DomUniverse that contains them. This means that:
326\list
327\li A DomItem always has some context: you can get the canonicalPath(), go up along it with
328 containingObject() and container().
329\li the indexing operator [], or the path(), field(), key() and index() methods (along with their
330 fields(), keys(), and indexes() contreparts) let one visit the contents of the current element.
331\li visitTree can be used to visit all subEments, if preferred on the top of it a visitor
332 pattern can also be used.
333\li If element specific attributes are wanted the two template casting as and ownerAs allow safe
334casting of the DomItem to a specific concrete type (cast to superclasses is not supported). \li
335Multithreading does not create issues, because even if an update replacing an OwningItem takes place
336the DomItem keeps a shared_ptr to the current owner as long as you use it \li Some elements (Empty,
337List, Map, ConstantData, Reference) might be inline, meaning that they are generated on the fly,
338wrapping data of the original object. \endlist
339
340One of the goals of the DomItem is to allow one to use real typed objects, as one is used to in C++,
341and also let one use modern C++ patterns, meaning container that contain the actual object (without
342pointer indirection).
343Exposed OwningItems are basically immutable, but during construction, objects can be modified.
344This will typically happen from a single thread, so there aren't locking issues, but pointers to
345inner elements might become invalid.
346In this case the use of the MutableDomItem is required.
347It does not keep any pointers to internal elements, but rather the path to them, and it resolves
348it every time it needs.
349*/
350
351FileToLoad::FileToLoad(const std::weak_ptr<DomEnvironment> &environment,
352 const QString &canonicalPath, const QString &logicalPath,
353 const std::optional<InMemoryContents> &content)
358{
359}
360
361FileToLoad FileToLoad::fromMemory(const std::weak_ptr<DomEnvironment> &environment,
362 const QString &path, const QString &code)
363{
364 const QString canonicalPath = QFileInfo(path).canonicalFilePath();
365 return {
366 environment,
367 // we can't lazy load files from an empty path, but non-existent paths where canonicalPath
368 // is empty are OK
369 canonicalPath.isEmpty() ? path : canonicalPath,
370 path,
371 InMemoryContents{ code },
372 };
373}
374
375FileToLoad FileToLoad::fromFileSystem(const std::weak_ptr<DomEnvironment> &environment,
376 const QString &path)
377{
378 // make the path canonical so the file content can be loaded from it later
379 const QString canonicalPath = QFileInfo(path).canonicalFilePath();
380 return {
381 environment,
382 canonicalPath,
383 path,
384 std::nullopt,
385 };
386}
387
390
392{
393 static ErrorGroups res = {{domErrorGroup}};
394 return res;
395}
396
398{
399 static ErrorGroups res = {{domErrorGroup, NewErrorGroup("Resolve")}};
400 return res;
401}
402
404{
405 Path res = visitEl([this](auto &&el) { return el->canonicalPath(*this); });
406 if (!(!res || res.headKind() == Path::Kind::Root)) {
407 qCWarning(domLog) << "non anchored canonical path:" << res.toString();
408 Q_ASSERT(false);
409 }
410 return res;
411
412}
413
415{
416 return visitEl([this](auto &&el) { return el->containingObject(*this); });
417}
418
419/*!
420 \internal
421 \brief Returns the QmlObject that this belongs to.
422
423 qmlObject() might also return the object of a component if GoTo:MostLikely is used.
424 */
425DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions) const
426{
427 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlObject; },
428 filterOptions))
429 return res;
430 if (options == GoTo::MostLikely) {
431 if (DomItem comp = component(options))
432 return comp.field(Fields::objects).index(0);
433 }
434 return DomItem();
435}
436
437DomItem DomItem::fileObject(GoTo options) const
438{
439 DomItem res = *this;
441 if (k == DomType::List || k == DomType::Map) {
442 res = res.containingObject();
443 k = res.internalKind();
444 }
445 if (k == DomType::ExternalItemInfo || (options == GoTo::MostLikely && k == DomType::ExternalItemPair))
446 return field(Fields::currentItem);
447 res = owner();
448 k = res.internalKind();
449 while (k != DomType::Empty) {
451 || k == DomType::JsFile)
452 break;
453 res = res.containingObject();
454 res = res.owner();
455 k = res.internalKind();
456 }
457 return res;
458}
459
460DomItem DomItem::rootQmlObject(GoTo options) const
461{
462 return qmlObject(options, FilterUpOptions::ReturnInner);
463}
464
466{
467 Path path = pathFromOwner();
468 if (!path)
469 path = canonicalPath();
470 Source s = path.split();
471 if (s.pathFromSource.length() > 1)
472 return containingObject().path(s.pathFromSource.dropTail());
473 return containingObject();
474}
475
477{
479 return *this;
481 if (shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
482 return env.copy(envPtr->ensureGlobalScopeWithName(env, envPtr->globalScopeName())->current,
483 Path());
484 }
485 return DomItem();
486}
487
488/*!
489 \internal
490 \brief The owner of an element, for an qmlObject this is the containing qml file.
491 */
493{
494 if (domTypeIsOwningItem(m_kind) || m_kind == DomType::Empty)
495 return *this;
496 return std::visit([this](auto &&el) {
497 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
498 return DomItem();
499 else
500 return DomItem(this->m_top, el, this->m_ownerPath, el.get());
501 }, m_owner);
502}
503
505{
506 if (domTypeIsTopItem(m_kind) || m_kind == DomType::Empty)
507 return *this;
508 return std::visit([](auto &&el) {
509 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
510 return DomItem();
511 else
512 return DomItem(el, el, Path(), el.get());
513 }, m_top);
514}
515
517{
518 DomItem res = top();
520 return res;
521 return DomItem(); // we are in the universe, and cannot go back to the environment...
522}
523
525{
526 DomItem res = top();
528 return res;
530 return res.field(Fields::universe);
531 return DomItem(); // we should be in an empty DomItem already...
532}
533
534/*!
535 \internal
536 Shorthand to obtain the ScriptExpression DomItem, in which this DomItem is defined.
537 Returns an empty DomItem if the item is not defined inside a ScriptExpression.
538 \sa goToFile()
539 */
541{
542 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::ScriptExpression; },
543 FilterUpOptions::ReturnOuter))
544 return res;
545 return DomItem();
546}
547
548/*!
549 \internal
550 Shorthand to obtain the QmlFile DomItem, in which this DomItem is defined.
551 Returns an empty DomItem if the item is not defined in a QML file.
552 \sa goToFile()
553 */
555{
556 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlFile; },
557 FilterUpOptions::ReturnOuter))
558 return res;
559 return DomItem();
560}
561
562/*!
563 \internal
564 Shorthand to obtain the QmlFile DomItem from a canonicalPath.
565 \sa containingFile()
566 */
567DomItem DomItem::goToFile(const QString &canonicalPath) const
568{
569 Q_UNUSED(canonicalPath);
570 DomItem file =
571 top().field(Fields::qmlFileWithPath).key(canonicalPath).field(Fields::currentItem);
572 return file;
573}
574
575/*!
576 \internal
577 In the DomItem hierarchy, go \c n levels up.
578 */
579DomItem DomItem::goUp(int n) const
580{
581 Path path = canonicalPath();
582 // first entry of path is usually top(), and you cannot go up from top().
583 if (path.length() < n + 1)
584 return DomItem();
585
586 DomItem parent = top().path(path.dropTail(n));
587 return parent;
588}
589
590/*!
591 \internal
592 In the DomItem hierarchy, go 1 level up to get the direct parent.
593 */
595{
596 return goUp(1);
597}
598
599/*!
600\internal
601Finds the first element in the DomItem hierarchy that satisfies filter.
602Use options to set the search direction, see also \l{FilterUpOptions}.
603*/
604DomItem DomItem::filterUp(function_ref<bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const
605{
606 if (options == FilterUpOptions::ReturnOuter && filter(internalKind(), *this)) {
607 return *this;
608 }
609
610 switch (options) {
613 for (DomItem current = *this, previous = DomItem(); current;
614 previous = current, current = current.directParent()) {
615 if (filter(current.internalKind(), current)) {
616 if (options != FilterUpOptions::ReturnOuterNoSelf || current != *this)
617 return current;
618 }
619 }
620 break;
621 }
623 DomItem current = top();
624 for (const Path &currentPath : canonicalPath()) {
625 current = current.path(currentPath);
626 if (filter(current.internalKind(), current))
627 return current;
628 }
629 break;
630 }
631
632 return DomItem();
633}
634
636{
637 DomItem res = filterUp([](DomType, const DomItem &el) { return el.isScope(); }, options);
638 return res;
639}
640
642{
643 QQmlJSScope::ConstPtr scope;
644 visitUp([&scope](const DomItem &item) {
645 scope = item.semanticScope();
646 return !scope; // stop when scope was true
647 });
648 return scope;
649}
650
652{
653 QQmlJSScope::ConstPtr scope = std::visit(
654 [](auto &&e) -> QQmlJSScope::ConstPtr {
655 using T = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
656 if constexpr (std::is_same_v<T, const QmlObject *>) {
657 return e->semanticScope();
658 } else if constexpr (std::is_same_v<T, const QmlComponent *>) {
659 return e->semanticScope();
660 } else if constexpr (std::is_same_v<T, const QmltypesComponent *>) {
661 return e->semanticScope();
662 } else if constexpr (std::is_same_v<T, SimpleObjectWrap>) {
663 if (const MethodInfo *mi = e->template as<MethodInfo>()) {
664 return mi->semanticScope();
665 }
666 if (const auto *propertyDefinition = e->template as<PropertyDefinition>()) {
667 return propertyDefinition->semanticScope();
668 }
669 } else if constexpr (std::is_same_v<T, ScriptElementDomWrapper>) {
670 return e.element().base()->semanticScope();
671 }
672 return {};
673 },
674 m_element);
675 return scope;
676}
677
678DomItem DomItem::get(const ErrorHandler &h, QList<Path> *visitedRefs) const
679{
680 if (const Reference *refPtr = as<Reference>())
681 return refPtr->get(*this, h, visitedRefs);
682 return DomItem();
683}
684
685QList<DomItem> DomItem::getAll(const ErrorHandler &h, QList<Path> *visitedRefs) const
686{
687 if (const Reference *refPtr = as<Reference>())
688 return refPtr->getAll(*this, h, visitedRefs);
689 return {};
690}
691
692PropertyInfo DomItem::propertyInfoWithName(const QString &name) const
693{
694 PropertyInfo pInfo;
695 visitPrototypeChain([&pInfo, name](const DomItem &obj) {
696 return obj.visitLocalSymbolsNamed(name, [&pInfo, name](const DomItem &el) {
697 switch (el.internalKind()) {
698 case DomType::Binding:
699 pInfo.bindings.append(el);
700 break;
701 case DomType::PropertyDefinition:
702 pInfo.propertyDefs.append(el);
703 break;
704 default:
705 break;
706 }
707 return true;
708 });
709 });
710 return pInfo;
711}
712
714{
715 QSet<QString> res;
716 visitPrototypeChain([&res](const DomItem &obj) {
717 res += obj.propertyDefs().keys();
718 res += obj.bindings().keys();
719 return true;
720 });
721 return res;
722}
723
724DomItem DomItem::component(GoTo options) const
725{
726 if (DomItem res = filterUp(
727 [](DomType kind, const DomItem &) {
728 return kind == DomType::QmlComponent || kind == DomType::QmltypesComponent
729 || kind == DomType::GlobalComponent;
730 },
731 FilterUpOptions::ReturnInner))
732 return res;
733 if (options == GoTo::MostLikely) {
734 DomItem item = *this;
735 DomType kind = item.internalKind();
736 if (kind == DomType::List || kind == DomType::Map) {
737 item = item.containingObject();
738 kind = item.internalKind();
739 }
740 switch (kind) {
741 case DomType::ExternalItemPair:
742 case DomType::ExternalItemInfo:
743 item = fileObject(options);
744 Q_FALLTHROUGH();
745 case DomType::QmlFile:
746 return item.field(Fields::components).key(QString()).index(0);
747 default:
748 break;
749 }
750 }
751 return DomItem();
752}
753
758
760{
761 static auto map = []() {
762 QMetaEnum metaEnum = QMetaEnum::fromType<LookupType>();
763 QMap<QString, LookupType> res;
764 for (int i = 0; i < metaEnum.keyCount(); ++i) {
765 res[QString::fromUtf8(metaEnum.key(i)).toLower()] = LookupType(metaEnum.value(i));
766 }
767 return res;
768 }();
769 return map;
770}
771
772static DomItem rootFromContext(const DomItem &root, PathRoot contextId)
773{
774 switch (contextId) {
776 return root.environment().field(Fields::moduleIndexWithUri);
777 case PathRoot::Cpp:
778 return root.environment()[Fields::qmltypesFileWithPath];
779 case PathRoot::Libs:
780 return root.environment()[Fields::plugins];
781 case PathRoot::Top:
782 return root.top();
783 case PathRoot::Env:
784 return root.environment();
786 return root.environment()[u"universe"];
787 default:
788 return DomItem{};
789 }
790}
791
792/*!
793 \internal
794 Is used to resolve the "get" field of QQmlJS::Dom::Reference. Reference contains a link to a type,
795 and its "get" field does return the DomItem for the linked type. For example, a type has a link to
796 its base type.
797*/
798static DomItem resolveReference(const DomItem &it, const Path &refRef, QList<Path> *visitedRefs,
799 const ErrorHandler &errorHandler, const ErrorGroups &errorGroup)
800{
801 if (visitedRefs->contains(refRef)) {
802 errorGroup
803 .error([visitedRefs, refRef](const Sink &sink) {
804 const QString msg = DomItem::tr("Circular reference:") + QLatin1Char('\n');
805 sink(QStringView{ msg });
806 for (const Path &vPath : *visitedRefs) {
807 sink(u" ");
808 vPath.dump(sink);
809 sink(u" >\n");
810 }
811 refRef.dump(sink);
812 })
813 .handle(errorHandler);
814 return {};
815 }
816
817 const Reference *ref = it.as<Reference>();
818 Q_ASSERT(ref);
819 visitedRefs->append(refRef);
820 DomItem resolveRes;
821 it.resolve(
822 ref->referredObjectPath,
823 [&resolveRes](Path, const DomItem &r) {
824 resolveRes = r;
825 return false;
826 },
827 errorHandler, ResolveOption::None, ref->referredObjectPath, visitedRefs);
828 return resolveRes;
829}
830
831static LookupOptions resolveLookupOptions(const PathCurrent &current, const DomItem &it)
832{
833 LookupOptions opt = LookupOption::Normal;
834 if (current == PathCurrent::LookupStrict)
835 return opt | LookupOption::Strict;
836
837 if (current == PathCurrent::Lookup) {
838 DomItem comp = it.component();
839 DomItem strict = comp.field(u"~strictLookup~");
840 if (!strict) {
841 DomItem env = it.environment();
842 strict = env.field(u"defaultStrictLookup");
843 }
844 if (strict && strict.value().toBool())
845 opt = opt | LookupOption::Strict;
846 }
847 return opt;
848}
849
850static std::optional<LookupType> lookupTypeFromString(const QString &expectedType)
851{
852 const auto &map = stringToLookupTypeMap();
853 auto it = map.find(expectedType.toLower());
854 if (it == map.end())
855 return {};
856 return *it;
857}
858
859bool DomItem::resolve(const Path &path, DomItem::Visitor visitor, const ErrorHandler &errorHandler,
860 ResolveOptions options, const Path &fullPath, QList<Path> *visitedRefs) const
861{
862 QList<Path> vRefs;
863 Path fPath = fullPath;
864 if (fullPath.length() == 0)
865 fPath = path;
866 if (path.length()==0)
867 return visitor(fPath, *this);
868 QList<QSet<quintptr>> visited(path.length() + 1);
869 Path myPath = path;
870 QList<ResolveToDo> toDos(1); // invariant: always increase pathIndex to guarantee end even with only partial visited match
871 if (path.headKind() == Path::Kind::Root) {
872 DomItem root = rootFromContext(*this, path.headRoot());
873 if (!root) {
875 .error(tr("Root context %1 is not known").arg(path.headName()))
876 .handle(errorHandler);
877 return false;
878 }
879 toDos[0] = {std::move(root), 1};
880 } else {
881 toDos[0] = {*this, 0};
882 }
883 while (!toDos.isEmpty()) {
884 const ResolveToDo toDo = toDos.takeLast();
885 {
886 auto idNow = toDo.item.id();
887 if (idNow == quintptr(0) && toDo.item == *this)
888 idNow = quintptr(this);
889 if (idNow != quintptr(0) && visited[0].contains(idNow))
890 continue;
891 }
892 int iPath = toDo.pathIndex;
893 DomItem it = toDo.item;
894 bool branchExhausted = false;
895 while (iPath < path.length() && it && !branchExhausted) {
896 auto idNow = it.id();
897 if (idNow == quintptr() && toDo.item == *this)
898 idNow = quintptr(this);
899 if (idNow != quintptr(0)) {
900 auto vPair = std::make_pair(idNow, iPath);
901 if (visited[vPair.second].contains(vPair.first))
902 break;
903 visited[vPair.second].insert(vPair.first);
904 }
905 if (options & ResolveOption::TraceVisit && !visitor(path.mid(0,iPath), it))
906 return false;
907 auto cNow = path[iPath++];
908 switch (cNow.headKind()) {
909 case Path::Kind::Key:
910 it = it.key(cNow.headName());
911 break;
913 if (cNow.checkHeadName(Fields::get) && it.internalKind() == DomType::Reference) {
914 it = resolveReference(it, it.canonicalPath(),
915 visitedRefs == nullptr ? &vRefs : visitedRefs,
916 errorHandler, myResolveErrors());
917 } else {
918 it = it.field(cNow.headName()); // avoid instantiation of QString?
919 }
920 break;
922 it = it.index(cNow.headIndex());
923 break;
925 {
926 // immediate expansion, might use extra memory, but simplifies code (no suspended evaluation)
927 Path toFind;
928 do {
929 if (iPath >= path.length()) {
930 myResolveErrors().warning(tr("Resolve with path ending with empty path, matches nothing."))
931 .handle(errorHandler);
932 branchExhausted = true; // allow and visit all?
933 break;
934 }
935 toFind = path[iPath++];
936 } while (toFind.headKind() == Path::Kind::Empty);
937 QList<Path::Kind> validFind({Path::Kind::Key, Path::Kind::Field, Path::Kind::Field, Path::Kind::Index});
938 if (!validFind.contains(toFind.headKind())) {
939 myResolveErrors().error(tr("After an empty path only key, field or indexes are supported, not %1.").arg(toFind.toString()))
940 .handle(errorHandler);
941 branchExhausted = true; // allow and visit all?
942 return false;
943 }
944 if (!branchExhausted)
945 visitTree(
946 Path(),
947 [&toFind, &toDos, iPath](Path, const DomItem &item, bool) {
948 // avoid non directly attached?
949 DomItem newItem = item[toFind];
950 if (newItem)
951 toDos.append({ std::move(newItem), iPath });
952 return true;
953 },
954 VisitOption::VisitSelf | VisitOption::Recurse
955 | VisitOption::VisitAdopted | VisitOption::NoPath);
956 branchExhausted = true;
957 break;
958 }
959 case Path::Kind::Root:
960 myResolveErrors().error(tr("Root path is supported only at the beginning, and only once, found %1 at %2 in %3")
961 .arg(cNow.toString()).arg(iPath -1).arg(path.toString())).handle(errorHandler);
962 return false;
964 {
965 PathCurrent current = cNow.headCurrent();
966 switch (current) {
968 // todo
969 case PathCurrent::Obj:
970 if (domKind() != DomKind::Object)
971 it = it.containingObject();
972 break;
974 bool cont = it.visitPrototypeChain(
975 [&toDos, iPath](const DomItem &subEl) {
976 toDos.append({ subEl, iPath });
977 return true;
978 },
979 VisitPrototypesOption::Normal, errorHandler, nullptr,
980 visitedRefs); // avoid passing visitedRefs?
981 if (!cont)
982 return false;
983 branchExhausted = true;
984 break;
985 }
987 bool cont = it.visitScopeChain(
988 [&toDos, iPath](const DomItem &subEl) {
989 toDos.append({ subEl, iPath });
990 return true;
991 },
992 LookupOption::Normal, errorHandler);
993 if (!cont)
994 return false;
995 branchExhausted = true;
996 break;
997 }
999 it = it.component();
1000 break;
1002 case PathCurrent::Ids:
1003 it = it.component().ids();
1004 break;
1005 case PathCurrent::Types:
1006 it = it.component()[Fields::exports];
1007 break;
1010 case PathCurrent::Lookup: {
1011 const LookupOptions opt = resolveLookupOptions(current, it);
1014 .error(tr("Javascript lookups not yet implemented"))
1015 .handle(errorHandler);
1016 return false;
1017 }
1018 // enter lookup
1019 auto idNow = it.id();
1020 if (idNow == quintptr(0) && toDo.item == *this)
1021 idNow = quintptr(this);
1022 if (idNow != quintptr(0)) {
1023 auto vPair = std::make_pair(idNow, iPath);
1024 if (visited[vPair.second].contains(vPair.first))
1025 break;
1026 visited[vPair.second].insert(vPair.first);
1027 }
1028 if (options & ResolveOption::TraceVisit && !visitor(path.mid(0, iPath), it))
1029 return false;
1030 if (iPath + 1 >= path.length()) {
1032 .error(tr("Premature end of path, expected a field specifying the "
1033 "type, and a key specifying the name to search after a "
1034 "lookup directive in %2")
1035 .arg(path.toString()))
1036 .handle(errorHandler);
1037 return false;
1038 }
1039 Path cNow = path[iPath++];
1040 if (cNow.headKind() != Path::Kind::Field) {
1042 .error(tr("Expected a key path specifying the type to search after "
1043 "a lookup directive, not %1 at component %2 of %3")
1044 .arg(cNow.toString())
1045 .arg(iPath)
1046 .arg(path.toString()))
1047 .handle(errorHandler);
1048 return false;
1049 }
1050 QString expectedType = cNow.headName();
1051 auto lookupType = lookupTypeFromString(expectedType);
1052 if (!lookupType) {
1054 .error(tr("Type for lookup was expected to be one of '%1', not "
1055 "%2")
1056 .arg(stringToLookupTypeMap().keys().join(", "_L1),
1057 expectedType))
1058 .handle(errorHandler);
1059 return false;
1060 }
1061 cNow = path[iPath++];
1062 if (cNow.headKind() != Path::Kind::Key) {
1064 .error(tr("Expected a key specifying the path to search after the "
1065 "@lookup directive and type, not %1 at component %2 of "
1066 "%3")
1067 .arg(cNow.toString())
1068 .arg(iPath)
1069 .arg(path.toString()))
1070 .handle(errorHandler);
1071 return false;
1072 }
1073 QString target = cNow.headName();
1074 if (target.isEmpty()) {
1076 .warning(tr("Path with empty lookup at component %1 of %2 will "
1077 "match nothing in %3.")
1078 .arg(iPath)
1079 .arg(path.toString())
1080 .arg(it.canonicalPath().toString()))
1081 .handle(errorHandler);
1082 return true;
1083 }
1084 it.visitLookup(
1085 target,
1086 [&toDos, iPath](const DomItem &subEl) {
1087 toDos.append({ subEl, iPath });
1088 return true;
1089 },
1090 *(std::move(lookupType)), opt, errorHandler, &(visited[iPath]),
1091 visitedRefs);
1092 branchExhausted = true;
1093 break;
1094 }
1095 }
1096 break;
1097 }
1098 case Path::Kind::Any:
1099 visitTree(
1100 Path(),
1101 [&toDos, iPath](Path, const DomItem &item, bool) {
1102 toDos.append({ item, iPath });
1103 return true;
1104 },
1105 VisitOption::VisitSelf | VisitOption::Recurse | VisitOption::VisitAdopted);
1106 branchExhausted = true;
1107 break;
1108 case Path::Kind::Filter:
1109 if (cNow.headFilter() && !cNow.headFilter()(it))
1110 branchExhausted = true;
1111 break;
1112 }
1113 }
1114 // visit the resolved path
1115 if (!branchExhausted && iPath == path.length() && !visitor(fPath, it))
1116 return false;
1117 }
1118 return true;
1119}
1120
1121DomItem DomItem::path(const Path &p, const ErrorHandler &errorHandler) const
1122{
1123 if (!p)
1124 return *this;
1125 DomItem res;
1126 resolve(p, [&res](const Path &, const DomItem &it) {
1127 res = it;
1128 return false;
1129 }, errorHandler);
1130 return res;
1131}
1132
1133DomItem DomItem::path(const QString &p, const ErrorHandler &errorHandler) const
1134{
1135 return path(Path::fromString(p, errorHandler));
1136}
1137
1138DomItem DomItem::path(QStringView p, const ErrorHandler &errorHandler) const
1139{
1140 return path(Path::fromString(p, errorHandler));
1141}
1142
1144{
1145 return visitEl([this](auto &&el) { return el->fields(*this); });
1146}
1147
1148DomItem DomItem::field(QStringView name) const
1149{
1150 return visitEl([this, name](auto &&el) { return el->field(*this, name); });
1151}
1152
1154{
1155 return visitEl([this](auto &&el) { return el->indexes(*this); });
1156}
1157
1158DomItem DomItem::index(index_type i) const
1159{
1160 return visitEl([this, i](auto &&el) { return el->index(*this, i); });
1161}
1162
1163bool DomItem::visitIndexes(function_ref<bool(const DomItem &)> visitor) const
1164{
1165 // use iterateDirectSubpaths instead?
1166 int nIndexes = indexes();
1167 for (int i = 0; i < nIndexes; ++i) {
1168 DomItem v = index(i);
1169 if (!visitor(v))
1170 return false;
1171 }
1172 return true;
1173}
1174
1176{
1177 return visitEl([this](auto &&el) { return el->keys(*this); });
1178}
1179
1181{
1182 QSet<QString> ks = keys();
1183 QStringList sortedKs(ks.begin(), ks.end());
1184 std::sort(sortedKs.begin(), sortedKs.end());
1185 return sortedKs;
1186}
1187
1188DomItem DomItem::key(const QString &name) const
1189{
1190 return visitEl([this, name](auto &&el) { return el->key(*this, name); });
1191}
1192
1193bool DomItem::visitKeys(function_ref<bool(const QString &, const DomItem &)> visitor) const
1194{
1195 // use iterateDirectSubpaths instead?
1196 const QStringList keys = sortedKeys();
1197 for (const QString &k : keys) {
1198 DomItem v = key(k);
1199 if (!visitor(k, v))
1200 return false;
1201 }
1202 return true;
1203}
1204
1206{
1207 QList<DomItem> res;
1208 iterateDirectSubpaths([&res](const PathEls::PathComponent &, function_ref<DomItem()> item) {
1209 res.append(item());
1210 return true;
1211 });
1212 return res;
1213}
1214
1215void DomItem::writeOutPre(OutWriter &ow) const
1216{
1217 if (hasAnnotations()) {
1218 DomItem anns = field(Fields::annotations);
1219 for (const auto &ann : anns.values()) {
1220 if (ann.annotations().indexes() == 0) {
1221 ow.ensureNewline();
1222 ann.writeOut(ow);
1223 ow.ensureNewline();
1224 } else {
1225 DomItem annAnn = ann.annotations();
1226 Q_ASSERT_X(annAnn.indexes() == 1 && annAnn.index(0).name() == u"duplicate",
1227 "DomItem::writeOutPre", "Unexpected annotation of annotation");
1228 }
1229 }
1230 }
1231 ow.itemStart(*this);
1232}
1233
1234void DomItem::writeOut(OutWriter &ow) const
1235{
1236 writeOutPre(ow);
1237 visitEl([this, &ow](auto &&el) { el->writeOut(*this, ow); });
1239}
1240
1241void DomItem::writeOutPost(OutWriter &ow) const
1242{
1243 ow.itemEnd();
1244}
1245
1246DomItem::WriteOutCheckResult DomItem::performWriteOutChecks(const DomItem &reformatted,
1247 OutWriter &ow,
1248 WriteOutChecks extraChecks) const
1249{
1250 auto compare = [this](const DomItem &obj1, const DomItem &obj2, QStringView obj2Name,
1251 const FieldFilter &filter) {
1252 const auto diffList = domCompareStrList(obj1, obj2, filter, DomCompareStrList::AllDiffs);
1253 if (!diffList.isEmpty()) {
1254 qCWarning(writeOutLog).noquote().nospace()
1255 << obj2Name << " writeOut of " << this->canonicalFilePath() << " has changes:\n"
1256 << diffList.join(QString());
1257 return false;
1258 }
1259 return true;
1260 };
1261 auto checkStability = [&ow, this](const QString &expected, const DomItem &obj,
1262 QStringView objName) {
1263 LineWriter lw2([](QStringView) {}, ow.lineWriter.fileName(), ow.lineWriter.options());
1264 OutWriter ow2(lw2);
1265 ow2.indentNextlines = true;
1266 obj.writeOut(ow2);
1267 ow2.eof();
1268 if (ow2.writtenStr != expected) {
1269 qCWarning(writeOutLog).noquote().nospace()
1270 << objName << " non stable writeOut of " << this->canonicalFilePath() << ":"
1271 << lineDiff(expected, ow2.writtenStr, 2);
1272 return false;
1273 }
1274 return true;
1275 };
1276
1277 if (extraChecks
1280 std::shared_ptr<DomEnvironment> newEnvPtr = newEnv.ownerAs<DomEnvironment>();
1281 if (!newEnvPtr)
1282 return WriteOutCheckResult::Failed;
1283
1284 auto newFilePtr = std::make_shared<QmlFile>(canonicalFilePath(), ow.writtenStr);
1285 newEnvPtr->addQmlFile(newFilePtr, AddOption::Overwrite);
1286
1287 DomItem newFile = newEnv.copy(newFilePtr, Path());
1288 if (newFilePtr->isValid()) {
1290 newEnvPtr->populateFromQmlFile(newFile);
1291 if ((extraChecks & WriteOutCheck::ReparseCompare)
1292 && !compare(reformatted, newFile, u"reparsed",
1294 return WriteOutCheckResult::Failed;
1295 if ((extraChecks & WriteOutCheck::ReparseStable))
1296 checkStability(ow.writtenStr, newFile, u"reparsed");
1297 }
1298 } else {
1299 const auto iterateErrors = [&newFile](const Sink &s) {
1300 newFile.iterateErrors(
1301 [s](const DomItem &, const ErrorMessage &msg) {
1302 s(u"\n ");
1303 msg.dump(s);
1304 return true;
1305 },
1306 true);
1307 s(u"\n"); // extra empty line at the end...
1308 };
1309 qCWarning(writeOutLog).noquote().nospace()
1310 << "writeOut of " << canonicalFilePath()
1311 << " created invalid code:\n----------\n"
1312 << ow.writtenStr << "\n----------" << iterateErrors;
1313 return WriteOutCheckResult::Failed;
1314 }
1315 }
1316 return WriteOutCheckResult::Success;
1317}
1318
1319/*!
1320 \internal
1321 Performes WriteOut of the FileItem and verifies the consistency of the DOM structure.
1322
1323 OutWriter is essentially a visitor traversing the DOM structure, starting from
1324 the current item representing a FileItem.
1325 While traversing it might be saving some intermediate information, used later for restoring
1326 written out item. Restoration is needed to validate that the DOM structure of the written item
1327 has not changed.
1328*/
1329bool DomItem::writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const
1330{
1331 ow.indentNextlines = true;
1332 auto currentFileItem = fileObject();
1333 writeOut(ow);
1334 ow.eof();
1335 WriteOutCheckResult result = WriteOutCheckResult::Success;
1336 if (extraChecks)
1337 result = performWriteOutChecks(currentFileItem, ow, extraChecks);
1338 return result == WriteOutCheckResult::Success ? bool(currentFileItem) : false;
1339}
1340
1341bool DomItem::writeOut(const QString &path, int nBackups, const LineWriterOptions &options,
1342 FileWriter *fw, WriteOutChecks extraChecks) const
1343{
1344 FileWriter localFw;
1345 if (!fw)
1346 fw = &localFw;
1347 auto status = fw->write(
1348 path,
1349 [this, path, &options, extraChecks](QTextStream &ts) {
1350 auto lw = createLineWriter([&ts](QStringView s) { ts << s; }, path, options);
1351 OutWriter ow(getFileItemOwner(fileObject()), *lw);
1352 return writeOutForFile(ow, extraChecks);
1353 },
1354 nBackups);
1355 switch (status) {
1356 case FileWriter::Status::DidWrite:
1357 case FileWriter::Status::SkippedEqual:
1358 return true;
1359 case FileWriter::Status::ShouldWrite:
1360 case FileWriter::Status::SkippedDueToFailure:
1361 qCWarning(writeOutLog) << "failure reformatting " << path;
1362 return false;
1363 default:
1364 qCWarning(writeOutLog) << "Unknown FileWriter::Status ";
1365 Q_ASSERT(false);
1366 return false;
1367 }
1368}
1369
1370bool DomItem::isCanonicalChild(const DomItem &item) const
1371{
1372 bool isChild = false;
1373 if (item.isOwningItem()) {
1375 } else {
1376 DomItem itemOw = item.owner();
1377 DomItem selfOw = owner();
1378 isChild = itemOw == selfOw && item.pathFromOwner().dropTail() == pathFromOwner();
1379 }
1380 return isChild;
1381}
1382
1384{
1385 bool hasAnnotations = false;
1386 DomType iKind = internalKind();
1387 switch (iKind) {
1388 case DomType::Id:
1389 if (const Id *myPtr = as<Id>())
1390 hasAnnotations = !myPtr->annotations.isEmpty();
1391 break;
1393 if (const PropertyDefinition *myPtr = as<PropertyDefinition>())
1394 hasAnnotations = !myPtr->annotations.isEmpty();
1395 break;
1397 if (const MethodInfo *myPtr = as<MethodInfo>())
1398 hasAnnotations = !myPtr->annotations.isEmpty();
1399 break;
1400 case DomType::QmlObject:
1401 if (const QmlObject *myPtr = as<QmlObject>())
1402 hasAnnotations = !myPtr->annotations().isEmpty();
1403 break;
1404 case DomType::Binding:
1405 if (const Binding *myPtr = as<Binding>())
1406 hasAnnotations = !myPtr->annotations().isEmpty();
1407 break;
1408 default:
1409 break;
1410 }
1411 return hasAnnotations;
1412}
1413
1414/*!
1415 \internal
1416 \brief Visits recursively all the children of this item using the given visitors.
1417
1418 First, the visitor is called and can continue or exit the visit by returning true or false.
1419
1420 Second, the openingVisitor is called and controls if the children of the current item needs to
1421 be visited or not by returning true or false. In either case, the visitation of all the other
1422 siblings is not affected. If both visitor and openingVisitor returned true, then the childrens of
1423 the current item will be recursively visited.
1424
1425 Finally, after all the children were visited by visitor and openingVisitor, the closingVisitor
1426 is called. Its return value is currently ignored.
1427
1428 Compared to the AST::Visitor*, openingVisitor and closingVisitor are called in the same order as
1429 the visit() and endVisit()-calls.
1430
1431 Filtering allows to not visit certain part of the trees, and is checked before(!) the lazy child
1432 is instantiated via its lambda. For example, visiting propertyInfos or defaultPropertyname takes
1433 a lot of time because it resolves and collects all properties inherited from base types, and
1434 might not even be relevant for the visitors.
1435 */
1436bool DomItem::visitTree(const Path &basePath, DomItem::ChildrenVisitor visitor,
1437 VisitOptions options, DomItem::ChildrenVisitor openingVisitor,
1438 DomItem::ChildrenVisitor closingVisitor, const FieldFilter &filter) const
1439{
1440 if (!*this)
1441 return true;
1442 if (options & VisitOption::VisitSelf && !visitor(basePath, *this, true))
1443 return false;
1444 if (options & VisitOption::VisitSelf && !openingVisitor(basePath, *this, true))
1445 return true;
1446 auto atEnd = qScopeGuard([closingVisitor, basePath, this, options]() {
1447 if (options & VisitOption::VisitSelf) {
1448 closingVisitor(basePath, *this, true);
1449 }
1450 });
1451 return iterateDirectSubpaths(
1452 [this, basePath, visitor, openingVisitor, closingVisitor, options,
1453 &filter](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) {
1454 Path pNow;
1455 if (!(options & VisitOption::NoPath)) {
1456 pNow = basePath;
1457 pNow = pNow.withComponent(c);
1458 }
1459 if (!filter(*this, c, DomItem{}))
1460 return true;
1461 DomItem item = itemF();
1462 bool directChild = isCanonicalChild(item);
1463 if (!directChild && !(options & VisitOption::VisitAdopted))
1464 return true;
1465 if (!directChild || !(options & VisitOption::Recurse)) {
1466 if (!visitor(pNow, item, directChild))
1467 return false;
1468 // give an option to avoid calling open/close when not recursing?
1469 // calling it always allows close to do the reverse looping (children before
1470 // parent)
1471 if (!openingVisitor(pNow, item, directChild))
1472 return true;
1473 closingVisitor(pNow, item, directChild);
1474 } else {
1475 return item.visitTree(pNow, visitor, options | VisitOption::VisitSelf,
1476 openingVisitor, closingVisitor, filter);
1477 }
1478 return true;
1479 });
1480}
1481static bool visitPrototypeIndex(QList<DomItem> &toDo, const DomItem &current,
1482 const DomItem &derivedFromPrototype, const ErrorHandler &h,
1483 QList<Path> *visitedRefs, VisitPrototypesOptions options,
1484 const DomItem &prototype)
1485{
1486 Path elId = prototype.canonicalPath();
1487 if (visitedRefs->contains(elId))
1488 return true;
1489 else
1490 visitedRefs->append(elId);
1491 QList<DomItem> protos = prototype.getAll(h, visitedRefs);
1492 if (protos.isEmpty()) {
1493 if (std::shared_ptr<DomEnvironment> envPtr =
1494 derivedFromPrototype.environment().ownerAs<DomEnvironment>())
1495 if (!(envPtr->options() & DomEnvironment::Option::NoDependencies))
1496 derivedFromPrototype.myErrors()
1497 .warning(derivedFromPrototype.tr("could not resolve prototype %1 (%2)")
1498 .arg(current.canonicalPath().toString(),
1499 prototype.field(Fields::referredObjectPath)
1500 .value()
1501 .toString()))
1502 .withItem(derivedFromPrototype)
1503 .handle(h);
1504 } else {
1505 if (protos.size() > 1) {
1506 QStringList protoPaths;
1507 for (const DomItem &p : protos)
1508 protoPaths.append(p.canonicalPath().toString());
1509 derivedFromPrototype.myErrors()
1510 .warning(derivedFromPrototype
1511 .tr("Multiple definitions found, using first only, resolving "
1512 "prototype %1 (%2): %3")
1513 .arg(current.canonicalPath().toString(),
1514 prototype.field(Fields::referredObjectPath)
1515 .value()
1516 .toString(),
1517 protoPaths.join(QLatin1String(", "))))
1518 .withItem(derivedFromPrototype)
1519 .handle(h);
1520 }
1521 int nProtos = 1; // change to protos.length() to use all prototypes
1522 // (sloppier)
1523 for (int i = nProtos; i != 0;) {
1524 DomItem proto = protos.at(--i);
1525 if (proto.internalKind() == DomType::Export) {
1527 proto = proto.proceedToScope(h, visitedRefs);
1528 toDo.append(proto);
1529 } else if (proto.internalKind() == DomType::QmlObject
1531 toDo.append(proto);
1532 } else {
1533 derivedFromPrototype.myErrors()
1534 .warning(derivedFromPrototype.tr("Unexpected prototype type %1 (%2)")
1535 .arg(current.canonicalPath().toString(),
1536 prototype.field(Fields::referredObjectPath)
1537 .value()
1538 .toString()))
1539 .withItem(derivedFromPrototype)
1540 .handle(h);
1541 }
1542 }
1543 }
1544 return true;
1545}
1546
1547bool DomItem::visitPrototypeChain(function_ref<bool(const DomItem &)> visitor,
1548 VisitPrototypesOptions options, const ErrorHandler &h,
1549 QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1550{
1551 QSet<quintptr> visitedLocal;
1552 if (!visited)
1553 visited = &visitedLocal;
1554 QList<Path> refsLocal;
1555 if (!visitedRefs)
1556 visitedRefs = &refsLocal;
1557 bool shouldVisit = !(options & VisitPrototypesOption::SkipFirst);
1558 DomItem current = qmlObject();
1559 if (!current) {
1560 myErrors().warning(tr("Prototype chain called outside object")).withItem(*this).handle(h);
1561 return true;
1562 }
1563 QList<DomItem> toDo({ current });
1564 while (!toDo.isEmpty()) {
1565 current = toDo.takeLast();
1566 current = current.proceedToScope(h, visitedRefs);
1567 if (visited->contains(current.id())) {
1568 // to warn about circular dependencies a purely local visited trace is required
1569 // as common ancestors of unrelated objects are valid and should be skipped
1570 // so we do not to warn unless requested
1573 .warning(tr("Detected multiple visit of %1 visiting prototypes of %2")
1574 .arg(current.canonicalPath().toString(),
1575 canonicalPath().toString()))
1576 .withItem(*this)
1577 .handle(h);
1578 continue;
1579 }
1580 visited->insert(current.id());
1581 if (shouldVisit && !visitor(current))
1582 return false;
1583 shouldVisit = true;
1584 current.field(Fields::prototypes)
1585 .visitIndexes([&toDo, &current, this, &h, visitedRefs, options](const DomItem &el) {
1586 return visitPrototypeIndex(toDo, current, *this, h, visitedRefs, options, el);
1587 });
1588 }
1589 return true;
1590}
1591
1593 function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options,
1594 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1595{
1596 // these are the scopes one can access with the . operator from the current location
1597 // but currently not the attached types, which we should
1599 if (k == DomType::QmlObject)
1600 return visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1601 if (visited && id() != 0) {
1602 if (visited->contains(id()))
1603 return true;
1604 visited->insert(id());
1605 }
1606 if (k == DomType::Id || k == DomType::Reference || k == DomType::Export) {
1607 // we go to the scope if it is clearly defined
1608 DomItem v = proceedToScope(h, visitedRefs);
1610 return v.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1611 }
1612 if (k == DomType::Binding) {
1613 // from a binding we can get to its value if it is a object
1614 DomItem v = field(Fields::value);
1616 return v.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1617 }
1618 if (k == DomType::PropertyDefinition) {
1619 // from a property definition we go to the type stored in it
1620 DomItem t = field(Fields::type).proceedToScope(h, visitedRefs);
1622 return t.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1623 }
1624 if (!(options & VisitPrototypesOption::SkipFirst) && isScope() && !visitor(*this))
1625 return false;
1626 return true;
1627}
1628
1629/*!
1630 * \brief DomItem::visitStaticTypePrototypeChains
1631 * \param visitor
1632 * \param visitFirst
1633 * \param visited
1634 * \return
1635 *
1636 * visit the values JS reaches accessing a type directly: the values if it is a singleton or the
1637 * attached type
1638 */
1640 function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options,
1641 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1642{
1643 QSet<quintptr> visitedLocal;
1644 if (!visited)
1645 visited = &visitedLocal;
1646 DomItem current = qmlObject();
1647 DomItem comp = current.component();
1648 if (comp.field(Fields::isSingleton).value().toBool(false)
1649 && !current.visitPrototypeChain(visitor, options, h, visited, visitedRefs))
1650 return false;
1651 if (DomItem attachedT = current.component().field(Fields::attachedType).field(Fields::get))
1652 if (!attachedT.visitPrototypeChain(
1653 visitor, options & ~VisitPrototypesOptions(VisitPrototypesOption::SkipFirst), h,
1654 visited, visitedRefs))
1655 return false;
1656 return true;
1657}
1658
1659/*!
1660 \brief Let the visitor visit the Dom Tree hierarchy of this DomItem.
1661 */
1662bool DomItem::visitUp(function_ref<bool(const DomItem &)> visitor) const
1663{
1665 while (p.length() > 0) {
1666 DomItem current = top().path(p);
1667 if (!visitor(current))
1668 return false;
1669 p = p.dropTail();
1670 }
1671 return true;
1672}
1673
1674/*!
1675 \brief Let the visitor visit the QML scope hierarchy of this DomItem.
1676 */
1678 function_ref<bool(const DomItem &)> visitor, LookupOptions options, const ErrorHandler &h,
1679 QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1680{
1681 QSet<quintptr> visitedLocal;
1682 if (!visited)
1683 visited = &visitedLocal;
1684 QList<Path> visitedRefsLocal;
1685 if (!visitedRefs)
1686 visitedRefs = &visitedRefsLocal;
1687 DomItem current = scope();
1688 if (!current) {
1689 myResolveErrors().warning(tr("Called visitScopeChain outside scopes")).handle(h);
1690 return true;
1691 }
1692 QList<DomItem> toDo { current };
1693 bool visitFirst = !(options & LookupOption::SkipFirstScope);
1694 bool visitCurrent = visitFirst;
1695 bool first = true;
1696 QSet<quintptr> alreadyAddedComponentMaps;
1697 while (!toDo.isEmpty()) {
1698 DomItem current = toDo.takeLast();
1699 if (visited->contains(current.id()))
1700 continue;
1701 visited->insert(current.id());
1702 if (visitCurrent && !visitor(current))
1703 return false;
1704 visitCurrent = true;
1705 switch (current.internalKind()) {
1706 case DomType::QmlObject: {
1707 if (!current.visitPrototypeChain(visitor, VisitPrototypesOption::SkipFirst, h, visited,
1708 visitedRefs))
1709 return false;
1710 DomItem root = current.rootQmlObject();
1711 if (root && root != current) {
1712 first = false;
1713 toDo.append(root);
1714 } else if (DomItem next = current.scope(
1715 FilterUpOptions::ReturnOuterNoSelf)) { // should be the component
1716 toDo.append(next);
1717 }
1718 } break;
1719 case DomType::ScriptExpression: // Js lexical scope
1720 first = false;
1722 toDo.append(next);
1723 break;
1724 case DomType::QmlComponent: { // ids/attached type
1725 if ((options & LookupOption::Strict) == 0) {
1726 if (DomItem comp = current.field(Fields::nextComponent))
1727 toDo.append(comp);
1728 }
1729 if (first && visitFirst && (options & LookupOption::VisitTopClassType)
1730 && *this == current) { // visit attached type if it is the top of the chain
1731 if (DomItem attachedT = current.field(Fields::attachedType).field(Fields::get))
1732 toDo.append(attachedT);
1733 }
1735 toDo.append(next);
1736
1737 DomItem owner = current.owner();
1738 Path pathToComponentMap = current.pathFromOwner().dropTail(2);
1739 DomItem componentMap = owner.path(pathToComponentMap);
1740 if (alreadyAddedComponentMaps.contains(componentMap.id()))
1741 break;
1742 alreadyAddedComponentMaps.insert(componentMap.id());
1743 const auto keys = componentMap.keys();
1744 for (const QString &x : keys) {
1745 DomItem componentList = componentMap.key(x);
1746 for (int i = 0; i < componentList.indexes(); ++i) {
1747 DomItem component = componentList.index(i);
1748 if (component != current && !visited->contains(component.id()))
1749 toDo.append(component);
1750 }
1751 }
1752 first = false;
1753 break;
1754 }
1755 case DomType::QmlFile: // subComponents, imported types
1756 if (DomItem iScope =
1757 current.field(Fields::importScope)) // treat file as a separate scope?
1758 toDo.append(iScope);
1759 first = false;
1760 break;
1761 case DomType::MethodInfo: // method arguments
1762 first = false;
1764 toDo.append(next);
1765 break;
1766 case DomType::ImportScope: // types
1767 first = false;
1768 if (auto globalC = globalScope().field(Fields::rootComponent))
1769 toDo.append(globalC);
1770 break;
1773 first = false;
1774 if (DomItem next = current.field(Fields::objects).index(0))
1775 toDo.append(next);
1776 break;
1778 first = false;
1779 break;
1780 default:
1781 first = false;
1783 .error(tr("Unexpected non scope object %1 (%2) reached in visitScopeChain")
1784 .arg(domTypeToString(current.internalKind()),
1785 current.canonicalPath().toString()))
1786 .handle(h);
1787 Q_ASSERT(false);
1788 break;
1789 }
1790 }
1791 return true;
1792}
1793
1795 const QString &symbolName, function_ref<bool(const DomItem &)> visitor, LookupOptions opts,
1796 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1797{
1798 return visitScopeChain(
1799 [symbolName, visitor](const DomItem &obj) {
1800 return obj.visitLocalSymbolsNamed(symbolName,
1801 [visitor](const DomItem &el) { return visitor(el); });
1802 },
1803 opts, h, visited, visitedRefs);
1804}
1805
1807{
1809public:
1810 CppTypeInfo() = default;
1811
1812 static CppTypeInfo fromString(QStringView target, const ErrorHandler &h = nullptr)
1813 {
1814 CppTypeInfo res;
1815 QRegularExpression reTarget = QRegularExpression(QRegularExpression::anchoredPattern(
1816 uR"(QList<(?<list>[a-zA-Z_0-9:]+) *(?<listPtr>\*?)>|QMap< *(?<mapKey>[a-zA-Z_0-9:]+) *, *(?<mapValue>[a-zA-Z_0-9:]+) *(?<mapPtr>\*?)>|(?<baseType>[a-zA-Z_0-9:]+) *(?<ptr>\*?))"));
1817
1818 QRegularExpressionMatch m = reTarget.matchView(target);
1819 if (!m.hasMatch()) {
1821 .error(tr("Unexpected complex CppType %1").arg(target))
1822 .handle(h);
1823 }
1824 res.baseType = m.captured(u"baseType");
1825 res.isPointer = !m.captured(u"ptr").isEmpty();
1826 if (!m.captured(u"list").isEmpty()) {
1827 res.isList = true;
1828 res.baseType = m.captured(u"list");
1829 res.isPointer = !m.captured(u"listPtr").isEmpty();
1830 }
1831 if (!m.captured(u"mapValue").isEmpty()) {
1832 res.isMap = true;
1833 if (m.captured(u"mapKey") != u"QString") {
1835 .error(tr("Unexpected complex CppType %1 (map with non QString key)")
1836 .arg(target))
1837 .handle(h);
1838 }
1839 res.baseType = m.captured(u"mapValue");
1840 res.isPointer = !m.captured(u"mapPtr").isEmpty();
1841 }
1842 return res;
1843 }
1844
1846 bool isPointer = false;
1847 bool isMap = false;
1848 bool isList = false;
1849};
1850
1851static bool visitForLookupType(const DomItem &el, LookupType lookupType,
1852 function_ref<bool(const DomItem &)> visitor)
1853{
1854 bool correctType = false;
1855 DomType iType = el.internalKind();
1856 switch (lookupType) {
1857 case LookupType::Binding:
1858 correctType = (iType == DomType::Binding);
1859 break;
1860 case LookupType::Method:
1861 correctType = (iType == DomType::MethodInfo);
1862 break;
1863 case LookupType::Property:
1864 correctType = (iType == DomType::PropertyDefinition || iType == DomType::Binding);
1865 break;
1866 case LookupType::PropertyDef:
1867 correctType = (iType == DomType::PropertyDefinition);
1868 break;
1869 case LookupType::Type:
1870 correctType = (iType == DomType::Export); // accept direct QmlObject ref?
1871 break;
1872 default:
1873 Q_ASSERT(false);
1874 break;
1875 }
1876 if (correctType)
1877 return visitor(el);
1878 return true;
1879}
1880
1882 const DomItem &newIt, const QStringList &subpath,
1883 function_ref<bool(const DomItem &)> visitor, LookupType lookupType,
1884 const ErrorHandler &errorHandler, QList<Path> *visitedRefs)
1885{
1886 QList<ResolveToDo> lookupToDos(
1887 { ResolveToDo{ newIt, 1 } }); // invariant: always increase pathIndex to guarantee
1888 // end even with only partial visited match
1889 QList<QSet<quintptr>> lookupVisited(subpath.size() + 1);
1890 while (!lookupToDos.isEmpty()) {
1891 ResolveToDo tNow = lookupToDos.takeFirst();
1892 auto vNow = std::make_pair(tNow.item.id(), tNow.pathIndex);
1893 DomItem subNow = tNow.item;
1894 int iSubPath = tNow.pathIndex;
1895 Q_ASSERT(iSubPath < subpath.size());
1896 QString subPathNow = subpath[iSubPath++];
1897 DomItem scope = subNow.proceedToScope();
1898 if (iSubPath < subpath.size()) {
1899 if (vNow.first != 0) {
1900 if (lookupVisited[vNow.second].contains(vNow.first))
1901 continue;
1902 else
1903 lookupVisited[vNow.second].insert(vNow.first);
1904 }
1906 scope.visitDirectAccessibleScopes(
1907 [&lookupToDos, &subPathNow, iSubPath](const DomItem &el) {
1908 return el.visitLocalSymbolsNamed(
1909 subPathNow, [&lookupToDos, iSubPath](const DomItem &subEl) {
1910 lookupToDos.append({ subEl, iSubPath });
1911 return true;
1912 });
1913 },
1914 VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]),
1915 visitedRefs);
1916 } else {
1917 bool cont = scope.visitDirectAccessibleScopes(
1918 [&visitor, &subPathNow, lookupType](const DomItem &el) -> bool {
1919 if (lookupType == LookupType::Symbol)
1920 return el.visitLocalSymbolsNamed(subPathNow, visitor);
1921 else
1922 return el.visitLocalSymbolsNamed(
1923 subPathNow, [lookupType, &visitor](const DomItem &el) -> bool {
1924 return visitForLookupType(el, lookupType, visitor);
1925 });
1926 },
1927 VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]),
1928 visitedRefs);
1929 if (!cont)
1930 return false;
1931 }
1932 }
1933 return true;
1934}
1935
1937 const QString &target, function_ref<bool(const DomItem &)> visitor, LookupType lookupType,
1938 LookupOptions opts, const ErrorHandler &errorHandler, QSet<quintptr> *visited,
1939 QList<Path> *visitedRefs) const
1940{
1941 if (target.isEmpty())
1942 return true;
1943 switch (lookupType) {
1944 case LookupType::Binding:
1945 case LookupType::Method:
1946 case LookupType::Property:
1947 case LookupType::PropertyDef:
1948 case LookupType::Symbol:
1949 case LookupType::Type: {
1950 QStringList subpath = target.split(QChar::fromLatin1('.'));
1951 if (subpath.size() == 1) {
1952 return visitLookup1(subpath.first(), visitor, opts, errorHandler, visited, visitedRefs);
1953 } else {
1954 return visitLookup1(
1955 subpath.at(0),
1956 [&subpath, visitor, lookupType, &errorHandler,
1957 visitedRefs](const DomItem &newIt) -> bool {
1958 return visitQualifiedNameLookup(newIt, subpath, visitor, lookupType,
1959 errorHandler, visitedRefs);
1960 },
1961 opts, errorHandler, visited, visitedRefs);
1962 }
1963 break;
1964 }
1965 case LookupType::CppType: {
1966 QString baseTarget = CppTypeInfo::fromString(target, errorHandler).baseType;
1967 DomItem localQmltypes = owner();
1968 while (localQmltypes && localQmltypes.internalKind() != DomType::QmltypesFile) {
1969 localQmltypes = localQmltypes.containingObject();
1970 localQmltypes = localQmltypes.owner();
1971 }
1972 if (localQmltypes) {
1973 if (DomItem localTypes = localQmltypes.field(Fields::components).key(baseTarget)) {
1974 bool cont = localTypes.visitIndexes([&visitor](const DomItem &els) {
1975 return els.visitIndexes([&visitor](const DomItem &el) {
1976 if (DomItem obj = el.field(Fields::objects).index(0))
1977 return visitor(obj);
1978 return true;
1979 });
1980 });
1981 if (!cont)
1982 return false;
1983 }
1984 }
1985 DomItem qmltypes = environment().field(Fields::qmltypesFileWithPath);
1986 return qmltypes.visitKeys([baseTarget, &visitor](const QString &, const DomItem &els) {
1987 DomItem comps =
1988 els.field(Fields::currentItem).field(Fields::components).key(baseTarget);
1989 return comps.visitIndexes([&visitor](const DomItem &el) {
1990 if (DomItem obj = el.field(Fields::objects).index(0))
1991 return visitor(obj);
1992 return true;
1993 });
1994 });
1995 break;
1996 }
1997 }
1998 Q_ASSERT(false);
1999 return true;
2000}
2001
2002/*!
2003 \internal
2004 \brief Dereference DomItems pointing to other DomItems.
2005
2006 Dereferences DomItems with internalKind being References, Export and Id.
2007 Also does multiple rounds of resolving for nested DomItems.
2008 Prefer this over \l {DomItem::get}.
2009 */
2010DomItem DomItem::proceedToScope(const ErrorHandler &h, QList<Path> *visitedRefs) const
2011{
2012 // follow references, resolve exports
2013 DomItem current = *this;
2014 while (current) {
2015 switch (current.internalKind()) {
2016 case DomType::Reference: {
2017 Path currentPath = current.canonicalPath();
2018 current = current.get(h, visitedRefs);
2019 break;
2020 }
2021 case DomType::Export:
2022 current = current.field(Fields::type);
2023 break;
2024 case DomType::Id:
2025 current = current.field(Fields::referredObject);
2026 break;
2027 default:
2028 return current.scope();
2029 break;
2030 }
2031 }
2032 return DomItem();
2033}
2034
2035QList<DomItem> DomItem::lookup(const QString &symbolName, LookupType type, LookupOptions opts,
2036 const ErrorHandler &errorHandler) const
2037{
2038 QList<DomItem> res;
2039 visitLookup(
2040 symbolName,
2041 [&res](const DomItem &el) {
2042 res.append(el);
2043 return true;
2044 },
2045 type, opts, errorHandler);
2046 return res;
2047}
2048
2049DomItem DomItem::lookupFirst(const QString &symbolName, LookupType type, LookupOptions opts,
2050 const ErrorHandler &errorHandler) const
2051{
2052 DomItem res;
2053 visitLookup(
2054 symbolName,
2055 [&res](const DomItem &el) {
2056 res = el;
2057 return false;
2058 },
2059 type, opts, errorHandler);
2060 return res;
2061}
2062
2064{
2065 return visitEl([](auto &&b) { return b->id(); });
2066}
2067
2069{
2070 return visitEl([](auto &&e) { return e->pathFromOwner(); });
2071}
2072
2074{
2075 return visitEl([this](auto &&e) { return e->canonicalFilePath(*this); });
2076}
2077
2079{
2080 if (m_kind == DomType::Empty)
2081 return MutableDomItem();
2082 DomItem o = owner();
2083 if (option == CopyOption::EnvDisconnected) {
2084 DomItem newItem = std::visit([this, &o](auto &&el) {
2085 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) {
2086 return DomItem();
2087 } else {
2088 auto copyPtr = el->makeCopy(o);
2089 return DomItem(m_top, copyPtr, m_ownerPath, copyPtr.get());
2090 }
2091 }, m_owner);
2093 }
2094 DomItem env = environment();
2095 std::shared_ptr<DomEnvironment> newEnvPtr;
2096 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
2097 newEnvPtr = std::make_shared<DomEnvironment>(envPtr, envPtr->loadPaths(), envPtr->options(),
2098 envPtr->domCreationOption());
2099 DomBase *eBase = envPtr.get();
2100 if (std::holds_alternative<const DomEnvironment *>(m_element) && eBase
2101 && std::get<const DomEnvironment *>(m_element) == eBase)
2102 return MutableDomItem(DomItem(newEnvPtr));
2103 } else if (std::shared_ptr<DomUniverse> univPtr = top().ownerAs<DomUniverse>()) {
2104 newEnvPtr = std::make_shared<DomEnvironment>(
2105 QStringList(),
2106 DomEnvironment::Option::SingleThreaded | DomEnvironment::Option::NoDependencies,
2108 } else {
2109 Q_ASSERT(false);
2110 return {};
2111 }
2112 DomItem newItem = std::visit(
2113 [this, newEnvPtr, &o](auto &&el) {
2114 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) {
2115 return DomItem();
2116 } else {
2117 auto copyPtr = el->makeCopy(o);
2118 return DomItem(newEnvPtr, copyPtr, m_ownerPath, copyPtr.get());
2119 }
2120 },
2121 m_owner);
2122
2123 switch (o.internalKind()) {
2125 newEnvPtr->addQmlDirectory(newItem.ownerAs<QmlDirectory>(), AddOption::Overwrite);
2126 break;
2127 case DomType::JsFile:
2128 newEnvPtr->addJsFile(newItem.ownerAs<JsFile>(), AddOption::Overwrite);
2129 break;
2130 case DomType::QmlFile:
2131 newEnvPtr->addQmlFile(newItem.ownerAs<QmlFile>(), AddOption::Overwrite);
2132 break;
2134 newEnvPtr->addQmltypesFile(newItem.ownerAs<QmltypesFile>(), AddOption::Overwrite);
2135 break;
2136 case DomType::GlobalScope: {
2137 newEnvPtr->addGlobalScope(newItem.ownerAs<GlobalScope>(), AddOption::Overwrite);
2138 } break;
2139 case DomType::ModuleIndex:
2140 case DomType::MockOwner:
2141 case DomType::ScriptExpression:
2142 case DomType::AstComments:
2143 case DomType::LoadInfo:
2144 case DomType::FileLocationsNode:
2145 case DomType::DomEnvironment:
2146 case DomType::DomUniverse:
2147 qCWarning(domLog) << "DomItem::makeCopy " << internalKindStr()
2148 << " does not support binding to environment";
2149 Q_ASSERT(false);
2150 return MutableDomItem();
2151 default:
2152 qCWarning(domLog) << "DomItem::makeCopy(" << internalKindStr()
2153 << ") is not an known OwningItem";
2154 Q_ASSERT(o.isOwningItem());
2155 return MutableDomItem();
2156 }
2157 DomItem newEnv(newEnvPtr);
2158 Q_ASSERT(newEnv.path(o.canonicalPath()).m_owner == newItem.m_owner);
2160}
2161
2162bool DomItem::commitToBase(const std::shared_ptr<DomEnvironment> &validEnvPtr) const
2163{
2164 DomItem env = environment();
2165 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
2166 return envPtr->commitToBase(env, validEnvPtr);
2167 }
2168 return false;
2169}
2170
2171bool DomItem::visitLocalSymbolsNamed(const QString &name, function_ref<bool(const DomItem &)> visitor) const
2172{
2173 if (name.isEmpty()) // no empty symbol
2174 return true;
2175 // we could avoid discriminating by type and just access all the needed stuff in the correct
2176 // sequence, making sure it is empty if not provided, but for now I find it clearer to check the
2177 // type
2178 DomItem f;
2179 DomItem v;
2180 switch (internalKind()) {
2181 case DomType::QmlObject:
2182 f = field(Fields::propertyDefs);
2183 v = f.key(name);
2184 if (!v.visitIndexes(visitor))
2185 return false;
2186 f = field(Fields::bindings);
2187 v = f.key(name);
2188 if (!v.visitIndexes(visitor))
2189 return false;
2190 f = field(Fields::methods);
2191 v = f.key(name);
2192 if (!v.visitIndexes(visitor))
2193 return false;
2194 break;
2196 // to do
2197 break;
2198 case DomType::QmlComponent:
2199 f = field(Fields::ids);
2200 v = f.key(name);
2201 if (!v.visitIndexes(visitor))
2202 return false;
2203 Q_FALLTHROUGH();
2204 case DomType::JsResource:
2205 case DomType::GlobalComponent:
2206 case DomType::QmltypesComponent:
2207 f = field(Fields::enumerations);
2208 v = f.key(name);
2209 if (!v.visitIndexes(visitor))
2210 return false;
2211 break;
2212 case DomType::MethodInfo: {
2213 DomItem params = field(Fields::parameters);
2214 if (!params.visitIndexes([name, visitor](const DomItem &p) {
2215 const MethodParameter *pPtr = p.as<MethodParameter>();
2216 if (pPtr->name == name && !visitor(p))
2217 return false;
2218 return true;
2219 }))
2220 return false;
2221 break;
2222 }
2223 case DomType::QmlFile: {
2224 f = field(Fields::components);
2225 v = f.key(name);
2226 if (!v.visitIndexes(visitor))
2227 return false;
2228 break;
2229 }
2230 case DomType::ImportScope: {
2231 f = field(Fields::imported);
2232 v = f.key(name);
2233 if (!v.visitIndexes(visitor))
2234 return false;
2235 f = field(Fields::qualifiedImports);
2236 v = f.key(name);
2237 if (v && !visitor(v))
2238 return false;
2239 break;
2240 default:
2241 Q_ASSERT(!isScope());
2242 break;
2243 }
2244 }
2245 return true;
2246}
2247
2248DomItem DomItem::operator[](const QString &cName) const
2249{
2250 if (internalKind() == DomType::Map)
2251 return key(cName);
2252 return field(cName);
2253}
2254
2255DomItem DomItem::operator[](QStringView cName) const
2256{
2257 if (internalKind() == DomType::Map)
2258 return key(cName.toString());
2259 return field(cName);
2260}
2261
2262DomItem DomItem::operator[](const Path &p) const
2263{
2264 return path(p);
2265}
2266
2268{
2269 return base()->value();
2270}
2271
2272void DomItem::dumpPtr(const Sink &sink) const
2273{
2274 sink(u"DomItem{ topPtr:");
2275 sink(QString::number((quintptr)topPtr().get(), 16));
2276 sink(u", ownerPtr:");
2277 sink(QString::number((quintptr)owningItemPtr().get(), 16));
2278 sink(u", ownerPath:");
2279 m_ownerPath.dump(sink);
2280 sink(u", elPtr:");
2281 sink(QString::number((quintptr)base(),16));
2282 sink(u"}");
2283}
2284
2286 const Sink &s, int indent,
2287 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2288{
2289 visitEl([this, s, indent, filter](auto &&e) { e->dump(*this, s, indent, filter); });
2290}
2291
2293DomItem::dump(const QString &path,
2294 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter,
2295 int nBackups, int indent, FileWriter *fw) const
2296{
2297 FileWriter localFw;
2298 if (!fw)
2299 fw = &localFw;
2300 switch (fw->write(
2301 path,
2302 [this, indent, filter](QTextStream &ts) {
2303 this->dump([&ts](QStringView s) { ts << s; }, indent, filter);
2304 return true;
2305 },
2306 nBackups)) {
2307 case FileWriter::Status::ShouldWrite:
2308 case FileWriter::Status::SkippedDueToFailure:
2309 qWarning() << "Failure dumping " << canonicalPath() << " to " << path;
2310 break;
2311 case FileWriter::Status::DidWrite:
2312 case FileWriter::Status::SkippedEqual:
2313 break;
2314 }
2315 return fw->status;
2316}
2317
2319{
2320 return dumperToString([this](const Sink &s){ dump(s); });
2321}
2322
2324{
2325 return std::visit([](auto &&ow) {
2326 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2327 return 0;
2328 else
2329 return ow->derivedFrom();
2330 }, m_owner);
2331}
2332
2333int DomItem::revision() const
2334{
2335 return std::visit([](auto &&ow) {
2336 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2337 return -1;
2338 else
2339 return ow->revision();
2340 }, m_owner);
2341}
2342
2344{
2345 return std::visit([](auto &&ow) {
2346 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2347 return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
2348 else
2349 return ow->createdAt();
2350 }, m_owner);
2351}
2352
2354{
2355 return std::visit([](auto &&ow) {
2356 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2357 return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
2358 else
2359 return ow->frozenAt();
2360 }, m_owner);
2361}
2362
2364{
2365 return std::visit([](auto &&ow) {
2366 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2367 return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
2368 else
2369 return ow->lastDataUpdateAt();
2370 }, m_owner);
2371}
2372
2374{
2375 std::visit([this, &msg](auto &&ow) {
2376 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2378 else
2379 ow->addError(owner(), std::move(msg.withItem(*this)));
2380 }, m_owner);
2381}
2382
2384{
2385 // We need a copy here. Error handlers may be called when this is gone.
2386 return [self = *this](const ErrorMessage &m) { self.addError(ErrorMessage(m)); };
2387}
2388
2389void DomItem::clearErrors(const ErrorGroups &groups, bool iterate) const
2390{
2391 std::visit([&groups](auto &&ow) {
2392 if constexpr (!std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2393 ow->clearErrors(groups);
2394 }, m_owner);
2395
2396 if (iterate) {
2397 iterateSubOwners([groups](const DomItem &i){
2398 i.clearErrors(groups, true);
2399 return true;
2400 });
2401 }
2402}
2403
2405 function_ref<bool(const DomItem &, const ErrorMessage &)> visitor, bool iterate,
2406 Path inPath) const
2407{
2408 if (!std::visit([this, visitor, inPath](auto &&el) {
2409 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2410 return true;
2411 else
2412 return el->iterateErrors(owner(), visitor, inPath);
2413 }, m_owner)) {
2414 return false;
2415 }
2416
2417 if (iterate && !iterateSubOwners([inPath, visitor](const DomItem &i) {
2418 return i.iterateErrors(visitor, true, inPath);
2419 })) {
2420 return false;
2421 }
2422
2423 return true;
2424}
2425
2426bool DomItem::iterateSubOwners(function_ref<bool(const DomItem &)> visitor) const
2427{
2428 return std::visit([this, visitor](auto &&o) {
2429 if constexpr (std::is_same_v<std::decay_t<decltype(o)>, std::monostate>)
2430 return true;
2431 else
2432 return o->iterateSubOwners(owner(), visitor);
2433 }, m_owner);
2434}
2435
2436bool DomItem::iterateDirectSubpaths(DirectVisitor v) const
2437{
2438 return visitEl(
2439 [this, v](auto &&el) { return el->iterateDirectSubpaths(*this, v); });
2440}
2441
2442DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, const QList<Path> &paths) const
2443{
2444 return subListItem(
2445 List::fromQList<Path>(pathFromOwner().withComponent(c), paths,
2446 [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
2447 return list.subReferenceItem(p, el);
2448 }));
2449}
2450
2451DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const
2452{
2454 return DomItem(m_top, m_owner, m_ownerPath, Reference(referencedObject, Path(c)));
2455 } else {
2456 return DomItem(m_top, m_owner, m_ownerPath,
2457 Reference(referencedObject, pathFromOwner().withComponent(c)));
2458 }
2459}
2460
2461shared_ptr<DomTop> DomItem::topPtr() const
2462{
2463 return std::visit([](auto &&el) -> shared_ptr<DomTop> {
2464 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2465 return {};
2466 else
2467 return el;
2468 }, m_top);
2469}
2470
2472{
2473 return std::visit([](auto &&el) -> shared_ptr<OwningItem> {
2474 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2475 return {};
2476 else
2477 return el;
2478 }, m_owner);
2479}
2480
2481/*!
2482 \internal
2483 Returns a pointer to the virtual base pointer to a DomBase.
2484*/
2485const DomBase *DomItem::base() const
2486{
2487 return visitEl([](auto &&el) -> const DomBase * { return el->domBase(); });
2488}
2489
2490DomItem::DomItem(const std::shared_ptr<DomEnvironment> &envPtr):
2491 DomItem(envPtr, envPtr, Path(), envPtr.get())
2492{
2493}
2494
2495DomItem::DomItem(const std::shared_ptr<DomUniverse> &universePtr):
2496 DomItem(universePtr, universePtr, Path(), universePtr.get())
2497{
2498}
2499
2500/*!
2501\brief Creates a new document with the given code
2502
2503This is mostly useful for testing or loading a single code snippet without any dependency.
2504The fileType should normally be QmlFile, but you might want to load a qmltypes file for
2505example and interpret it as qmltypes file (not plain Qml), or as JsFile. In those case
2506set the file type accordingly.
2507*/
2508DomItem DomItem::fromCode(const QString &code, DomType fileType)
2509{
2510 if (code.isEmpty())
2511 return DomItem();
2512
2513 auto env = DomEnvironment::create({}, DomEnvironment::Option::NoDependencies);
2514 DomItem tFile;
2515 env->loadFile(
2516 FileToLoad::fromMemory(env, QString(), code),
2517 [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
2518 std::make_optional(fileType));
2519 return tFile.fileObject();
2520}
2521
2522Empty::Empty()
2523{}
2524
2525Path Empty::pathFromOwner() const
2526{
2527 return Path();
2528}
2529
2530Path Empty::canonicalPath(const DomItem &) const
2531{
2532 return Path();
2533}
2534
2536{
2537 return true;
2538}
2539
2540DomItem Empty::containingObject(const DomItem &self) const
2541{
2542 return self;
2543}
2544
2545void Empty::dump(
2546 const DomItem &, const Sink &s, int,
2547 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>) const
2548{
2549 s(u"null");
2550}
2551
2552Map::Map(const Path &pathFromOwner, const Map::LookupFunction &lookup,
2553 const Keys &keys, const QString &targetType)
2555{}
2556
2558{
2559 return quintptr(0);
2560}
2561
2562bool Map::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2563{
2564 QSet<QString> ksSet = keys(self);
2565 QStringList ksList = QStringList(ksSet.begin(), ksSet.end());
2566 std::sort(ksList.begin(), ksList.end());
2567 for (const QString &k : std::as_const(ksList)) {
2568 if (!visitor(PathEls::Key(k), [&self, this, k]() { return key(self, k); }))
2569 return false;
2570 }
2571 return true;
2572}
2573
2574const QSet<QString> Map::keys(const DomItem &self) const
2575{
2576 return m_keys(self);
2577}
2578
2579DomItem Map::key(const DomItem &self, const QString &name) const
2580{
2581 return m_lookup(self, name);
2582}
2583
2585 const DomItem &self, const Sink &sink, int indent,
2586 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2587{
2588 bool comma = false;
2589 DomKind dK = self.domKind();
2590 switch (dK) {
2591 case DomKind::Object:
2592 sink(u"{ \"~type~\":");
2593 sinkEscaped(sink, typeName());
2594 comma = true;
2595 break;
2596 case DomKind::Value:
2597 {
2598 sink(QString::fromUtf8(value().toJsonValue().toJson()));
2599 break;
2600 }
2601 case DomKind::Empty:
2602 sink(u"null");
2603 break;
2604 case DomKind::List:
2605 sink(u"[");
2606 break;
2607 case DomKind::Map:
2608 sink(u"{");
2609 break;
2610 case DomKind::ScriptElement:
2611 // nothing to print
2612 break;
2613 }
2614 auto closeParens = qScopeGuard(
2615 [dK, sink, indent]{
2616 switch (dK) {
2617 case DomKind::Object:
2618 sinkNewline(sink, indent);
2619 sink(u"}");
2620 break;
2621 case DomKind::Value:
2622 break;
2623 case DomKind::Empty:
2624 break;
2625 case DomKind::List:
2626 sinkNewline(sink, indent);
2627 sink(u"]");
2628 break;
2629 case DomKind::Map:
2630 sinkNewline(sink, indent);
2631 sink(u"}");
2632 break;
2633 case DomKind::ScriptElement:
2634 // nothing to print
2635 break;
2636 }
2637 });
2638 index_type idx = 0;
2639 self.iterateDirectSubpaths(
2640 [&comma, &idx, dK, sink, indent, &self, filter](const PathEls::PathComponent &c,
2641 function_ref<DomItem()> itemF) {
2642 DomItem i = itemF();
2643 if (!filter(self, c, i))
2644 return true;
2645 if (comma)
2646 sink(u",");
2647 else
2648 comma = true;
2649 switch (c.kind()) {
2650 case Path::Kind::Field:
2651 sinkNewline(sink, indent + 2);
2652 if (dK != DomKind::Object)
2653 sink(u"UNEXPECTED ENTRY ERROR:");
2654 sinkEscaped(sink, c.name());
2655 sink(u":");
2656 break;
2657 case Path::Kind::Key:
2658 sinkNewline(sink, indent + 2);
2659 if (dK != DomKind::Map)
2660 sink(u"UNEXPECTED ENTRY ERROR:");
2661 sinkEscaped(sink, c.name());
2662 sink(u":");
2663 break;
2664 case Path::Kind::Index:
2665 sinkNewline(sink, indent + 2);
2666 if (dK != DomKind::List)
2667 sink(u"UNEXPECTED ENTRY ERROR:");
2668 else if (idx++ != c.index())
2669 sink(u"OUT OF ORDER ARRAY:");
2670 break;
2671 default:
2672 sinkNewline(sink, indent + 2);
2673 sink(u"UNEXPECTED PATH KIND ERROR (ignored)");
2674 break;
2675 }
2676 if (self.isCanonicalChild(i)) {
2677 i.dump(sink, indent + 2, filter);
2678 } else {
2679 sink(uR"({ "~type~": "Reference", "immediate": true, "referredObjectPath":")");
2680 i.canonicalPath().dump([sink](QStringView s) {
2681 sinkEscaped(sink, s, EscapeOptions::NoOuterQuotes);
2682 });
2683 sink(u"\"}");
2684 }
2685 return true;
2686 });
2687}
2688
2689List::List(const Path &pathFromOwner, const List::LookupFunction &lookup,
2690 const List::Length &length, const List::IteratorFunction &iterator,
2691 const QString &elType):
2694{}
2695
2697{
2698 return quintptr(0);
2699}
2700
2702 const DomItem &self, const Sink &sink, int indent,
2703 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2704{
2705 bool first = true;
2706 sink(u"[");
2707 iterateDirectSubpaths(
2708 self,
2709 [&self, indent, &first, sink, filter](const PathEls::PathComponent &c,
2710 function_ref<DomItem()> itemF) {
2711 DomItem item = itemF();
2712 if (!filter(self, c, item))
2713 return true;
2714 if (first)
2715 first = false;
2716 else
2717 sink(u",");
2718 sinkNewline(sink, indent + 2);
2719 item.dump(sink, indent + 2, filter);
2720 return true;
2721 });
2722 sink(u"]");
2723}
2724
2725bool List::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2726{
2727 if (m_iterator) {
2728 return m_iterator(self, [visitor](index_type i, function_ref<DomItem()> itemF) {
2729 return visitor(PathEls::Index(i), itemF);
2730 });
2731 }
2732 index_type len = indexes(self);
2733 for (index_type i = 0; i < len; ++i) {
2734 if (!visitor(PathEls::Index(i), [this, &self, i]() { return index(self, i); }))
2735 return false;
2736 }
2737 return true;
2738}
2739
2740index_type List::indexes(const DomItem &self) const
2741{
2742 return m_length(self);
2743}
2744
2745DomItem List::index(const DomItem &self, index_type index) const
2746{
2747 return m_lookup(self, index);
2748}
2749
2750void List::writeOut(const DomItem &self, OutWriter &ow, bool compact) const
2751{
2752 const auto fLoc = FileLocations::treeOf(self);
2753 ow.writeRegion(fLoc, LeftBracketRegion);
2754 bool first = true;
2755 iterateDirectSubpaths(self,
2756 [&ow, &first, &fLoc, compact](const PathEls::PathComponent &,
2757 function_ref<DomItem()> elF) {
2758 if (first)
2759 first = false;
2760 else
2761 ow.writeRegion(fLoc, CommaTokenRegion).ensureSpace();
2762 if (!compact)
2763 ow.ensureNewline(1);
2764 DomItem el = elF();
2765 el.writeOut(ow);
2766 return true;
2767 });
2768 if (!compact && !first)
2769 ow.newline();
2770 ow.writeRegion(fLoc, RightBracketRegion);
2771}
2772
2774
2776{
2777 Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
2778 return self.owner().canonicalPath().withPath(m_pathFromOwner);
2779}
2780
2782{
2783 Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
2784 return DomBase::containingObject(self);
2785}
2786
2788{
2789 //if (!domTypeCanBeInline(kind()))
2790 m_pathFromOwner = newPath;
2791}
2792
2794{
2795 for (const Path &p : referredObjectPath) {
2796 switch (p.headKind()) {
2797 case Path::Kind::Current:
2798 switch (p.headCurrent()) {
2799 case PathCurrent::Lookup:
2800 case PathCurrent::LookupDynamic:
2801 case PathCurrent::LookupStrict:
2802 case PathCurrent::ObjChain:
2803 case PathCurrent::ScopeChain:
2804 return true;
2805 default:
2806 break;
2807 }
2808 break;
2809 case Path::Kind::Empty:
2810 case Path::Kind::Any:
2811 case Path::Kind::Filter:
2812 return true;
2813 default:
2814 break;
2815 }
2816 }
2817 return false;
2818}
2819
2820Reference::Reference(const Path &referredObject, const Path &pathFromOwner, const SourceLocation &)
2822{
2823}
2824
2826{
2827 return quintptr(0);
2828}
2829
2830bool Reference::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2831{
2832 bool cont = true;
2833 cont = cont && self.invokeVisitorOnLazyField(visitor, Fields::referredObjectPath, [this]() {
2834 return referredObjectPath.toString();
2835 });
2836 cont = cont && visitor(PathEls::Field(Fields::get), [this, &self]() { return this->get(self); });
2837 return cont;
2838}
2839
2840DomItem Reference::field(const DomItem &self, QStringView name) const
2841{
2842 if (Fields::referredObjectPath == name) {
2843 return self.subDataItem(PathEls::Field(Fields::referredObjectPath),
2844 referredObjectPath.toString());
2845 }
2846 if (Fields::get == name)
2847 return get(self);
2848 return DomItem();
2849}
2850
2852{
2853 return QList<QString>({Fields::referredObjectPath.toString(), Fields::get.toString()});
2854}
2855
2857{
2858 return DomItem();
2859}
2860
2861DomItem Reference::key(const DomItem &, const QString &) const
2862{
2863 return DomItem();
2864}
2865
2866DomItem Reference::get(const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const
2867{
2868 DomItem res;
2869 if (referredObjectPath) {
2870 DomItem env;
2871 Path selfPath;
2872 Path cachedPath;
2873 if (shouldCache()) {
2874 env = self.environment();
2875 if (env) {
2876 selfPath = self.canonicalPath();
2877 RefCacheEntry cached = RefCacheEntry::forPath(self, selfPath);
2878 switch (cached.cached) {
2879 case RefCacheEntry::Cached::None:
2880 break;
2881 case RefCacheEntry::Cached::First:
2882 case RefCacheEntry::Cached::All:
2883 if (!cached.canonicalPaths.isEmpty())
2884 cachedPath = cached.canonicalPaths.first();
2885 else
2886 return res;
2887 break;
2888 }
2889 if (cachedPath) {
2890 res = env.path(cachedPath);
2891 if (!res)
2892 qCWarning(refLog) << "referenceCache outdated, reference at " << selfPath
2893 << " leads to invalid path " << cachedPath;
2894 else
2895 return res;
2896 }
2897 }
2898 }
2899 QList<Path> visitedRefsLocal;
2900 self.resolve(
2901 referredObjectPath,
2902 [&res](Path, const DomItem &el) {
2903 res = el;
2904 return false;
2905 },
2906 h, ResolveOption::None, referredObjectPath,
2907 (visitedRefs ? visitedRefs : &visitedRefsLocal));
2908 if (env)
2910 env, selfPath, RefCacheEntry { RefCacheEntry::Cached::First, { cachedPath } });
2911 }
2912 return res;
2913}
2914
2916 const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const
2917{
2918 QList<DomItem> res;
2919 if (referredObjectPath) {
2920 DomItem env;
2921 Path selfPath;
2922 QList<Path> cachedPaths;
2923 if (shouldCache()) {
2924 selfPath = canonicalPath(self);
2925 env = self.environment();
2926 RefCacheEntry cached = RefCacheEntry::forPath(env, selfPath);
2927 switch (cached.cached) {
2928 case RefCacheEntry::Cached::None:
2929 case RefCacheEntry::Cached::First:
2930 break;
2931 case RefCacheEntry::Cached::All:
2932 cachedPaths += cached.canonicalPaths;
2933 if (cachedPaths.isEmpty())
2934 return res;
2935 }
2936 }
2937 if (!cachedPaths.isEmpty()) {
2938 bool outdated = false;
2939 for (const Path &p : cachedPaths) {
2940 DomItem newEl = env.path(p);
2941 if (!newEl) {
2942 outdated = true;
2943 qCWarning(refLog) << "referenceCache outdated, reference at " << selfPath
2944 << " leads to invalid path " << p;
2945 break;
2946 } else {
2947 res.append(newEl);
2948 }
2949 }
2950 if (outdated) {
2951 res.clear();
2952 } else {
2953 return res;
2954 }
2955 }
2956 self.resolve(
2957 referredObjectPath,
2958 [&res](Path, const DomItem &el) {
2959 res.append(el);
2960 return true;
2961 },
2962 h, ResolveOption::None, referredObjectPath, visitedRefs);
2963 if (env) {
2964 QList<Path> canonicalPaths;
2965 for (const DomItem &i : res) {
2966 if (i)
2967 canonicalPaths.append(i.canonicalPath());
2968 else
2969 qCWarning(refLog)
2970 << "getAll of reference at " << selfPath << " visits empty items.";
2971 }
2973 env, selfPath,
2974 RefCacheEntry { RefCacheEntry::Cached::All, std::move(canonicalPaths) });
2975 }
2976 }
2977 return res;
2978}
2979
2980/*!
2981\internal
2982\class QQmlJS::Dom::OwningItem
2983
2984\brief A DomItem that owns other DomItems and is managed through a shared pointer
2985
2986This is the unit of update of the Dom model, only OwningItem can be updated after
2987exposed, to update a single element one has to copy its owner, change it, and expose an new one.
2988The non owning contents of an exposed OwiningItem can safely be considered immutable, and pointers
2989to them must remain valid for the whole lifetime of the OwningItem (that is managed with
2990shared_ptr), only the sub OwningItems *might* be change.
2991The OwningItem has a mutex that controls it access (mainly for the errors, observers, and sub
2992OwningItems), Access to the rest is *not* controlled, it should either be by a single thread
2993(during construction), or immutable (after pubblication).
2994
2995*/
2996
2997OwningItem::OwningItem(int derivedFrom)
2998 : m_derivedFrom(derivedFrom),
2999 m_revision(nextRevision()),
3003{}
3004
3005OwningItem::OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt)
3006 : m_derivedFrom(derivedFrom),
3007 m_revision(nextRevision()),
3011{}
3012
3014 : m_derivedFrom(o.revision()),
3015 m_revision(nextRevision()),
3019{
3020 QMultiMap<Path, ErrorMessage> my_errors;
3021 {
3022 QMutexLocker l1(o.mutex());
3023 my_errors = o.m_errors;
3024
3025 }
3026 {
3027 QMutexLocker l2(mutex());
3028 m_errors = my_errors;
3029 }
3030}
3031
3032
3034{
3035 static QAtomicInt nextRev(0);
3036 return ++nextRev;
3037}
3038
3039bool OwningItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
3040{
3041 bool cont = true;
3042 cont = cont && visitor(PathEls::Field(Fields::errors), [&self, this]() {
3043 QMultiMap<Path, ErrorMessage> myErrors = localErrors();
3044 return self.subMapItem(Map(
3045 self.pathFromOwner().withField(Fields::errors),
3046 [myErrors](const DomItem &map, const QString &key) {
3047 auto it = myErrors.find(Path::fromString(key));
3048 if (it != myErrors.end())
3049 return map.subDataItem(PathEls::Key(key), it->toCbor(),
3050 ConstantData::Options::FirstMapIsFields);
3051 else
3052 return DomItem();
3053 },
3054 [myErrors](const DomItem &) {
3055 QSet<QString> res;
3056 auto it = myErrors.keyBegin();
3057 auto end = myErrors.keyEnd();
3058 while (it != end)
3059 res.insert(it++->toString());
3060 return res;
3061 },
3062 QLatin1String("ErrorMessages")));
3063 });
3064 return cont;
3065}
3066
3068{
3070 if (s.pathFromSource) {
3071 if (!s.pathToSource)
3072 return DomItem();
3073 return self.path(s.pathToSource);
3074 }
3075 return DomItem();
3076}
3077
3079{
3080 return m_derivedFrom;
3081}
3082
3084{
3085 return m_revision;
3086}
3087
3088bool OwningItem::frozen() const
3089{
3090 return m_frozenAt > m_createdAt;
3091}
3092
3094{
3095 if (!frozen()) {
3096 m_frozenAt = QDateTime::currentDateTimeUtc();
3097 if (m_frozenAt <= m_createdAt)
3098 m_frozenAt = m_createdAt.addSecs(1);
3099 return true;
3100 }
3101 return false;
3102}
3103
3105{
3106 return m_createdAt;
3107}
3108
3110{
3111 return m_lastDataUpdateAt;
3112}
3113
3115{
3116 return m_frozenAt;
3117}
3118
3119void OwningItem::refreshedDataAt(QDateTime tNew)
3120{
3121 if (m_lastDataUpdateAt < tNew) // remove check?
3122 m_lastDataUpdateAt = tNew;
3123}
3124
3126{
3127 addErrorLocal(std::move(msg));
3128}
3129
3131{
3132 QMutexLocker l(mutex());
3133 quint32 &c = m_errorsCounts[msg];
3134 c += 1;
3135 if (c == 1)
3136 m_errors.insert(msg.path, msg);
3137}
3138
3140{
3141 QMutexLocker l(mutex());
3142 auto it = m_errors.begin();
3143 while (it != m_errors.end()) {
3144 if (it->errorGroups == groups)
3145 it = m_errors.erase(it);
3146 else
3147 ++it;
3148 }
3149}
3150
3152 const DomItem &self, function_ref<bool(const DomItem &, const ErrorMessage &)> visitor,
3153 const Path &inPath)
3154{
3155 QMultiMap<Path, ErrorMessage> myErrors;
3156 {
3157 QMutexLocker l(mutex());
3158 myErrors = m_errors;
3159 }
3160 auto it = myErrors.lowerBound(inPath);
3161 auto end = myErrors.end();
3162 while (it != end && it.key().mid(0, inPath.length()) == inPath) {
3163 if (!visitor(self, *it++))
3164 return false;
3165 }
3166 return true;
3167}
3168
3169bool OwningItem::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor)
3170{
3171 return self.iterateDirectSubpaths(
3172 [&self, visitor](const PathEls::PathComponent &, function_ref<DomItem()> iF) {
3173 DomItem i = iF();
3174 if (i.owningItemPtr() != self.owningItemPtr()) {
3175 DomItem container = i.container();
3176 if (container.id() == self.id())
3177 return visitor(i);
3178 }
3179 return true;
3180 });
3181}
3182
3183bool operator==(const DomItem &o1, const DomItem &o2)
3184{
3185 if (o1.m_kind != o2.m_kind)
3186 return false;
3187 return o1.visitEl([&o1, &o2](auto &&el1) {
3188 auto &&el2 = std::get<std::decay_t<decltype(el1)>>(o2.m_element);
3189 auto id1 = el1->id();
3190 auto id2 = el2->id();
3191 if (id1 != id2)
3192 return false;
3193 if (id1 != quintptr(0))
3194 return true;
3195 if (o1.m_owner != o2.m_owner)
3196 return false;
3197 Path p1 = el1->pathFromOwner();
3198 Path p2 = el2->pathFromOwner();
3199 if (p1 != p2)
3200 return false;
3201 return true;
3202 });
3203}
3204
3206{
3207 MutableDomItem self;
3208 return [&self](const ErrorMessage &m) { self.addError(ErrorMessage(m)); };
3209}
3210
3212 const PropertyDefinition &propertyDef, AddOption option)
3213{
3214 if (QmlObject *el = mutableAs<QmlObject>())
3215 return el->addPropertyDef(*this, propertyDef, option);
3216 else
3217 Q_ASSERT(false && "addPropertyDef on non qml scope");
3218 return MutableDomItem();
3219}
3220
3222{
3223 if (QmlObject *el = mutableAs<QmlObject>())
3224 return el->addBinding(*this, binding, option);
3225 else
3226 Q_ASSERT(false && "addBinding on non qml scope");
3227 return MutableDomItem();
3228}
3229
3231{
3232 if (QmlObject *el = mutableAs<QmlObject>())
3233 return el->addMethod(*this, functionDef, option);
3234 else
3235 Q_ASSERT(false && "addMethod on non qml scope");
3236 return MutableDomItem();
3237}
3238
3240{
3241 if (QmlObject *el = mutableAs<QmlObject>()) {
3242 return el->addChild(*this, child);
3243 } else if (QmlComponent *el = mutableAs<QmlComponent>()) {
3244 Path p = el->addObject(child);
3245 return owner().path(p); // convenience: treat component objects as children
3246 } else {
3247 Q_ASSERT(false && "addChild on non qml scope");
3248 }
3249 return MutableDomItem();
3250}
3251
3252
3253QDebug operator<<(QDebug debug, const DomItem &c)
3254{
3255 dumperToQDebug([&c](const Sink &s) { c.dump(s); }, debug);
3256 return debug;
3257}
3258
3259QDebug operator<<(QDebug debug, const MutableDomItem &c)
3260{
3261 MutableDomItem cc(c);
3262 return debug.noquote().nospace() << "MutableDomItem(" << domTypeToString(cc.internalKind())
3263 << ", " << cc.canonicalPath().toString() << ")";
3264}
3265
3266bool ListPBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const
3267{
3268 index_type len = index_type(m_pList.size());
3269 for (index_type i = 0; i < len; ++i) {
3270 if (!v(PathEls::Index(i), [this, &self, i] { return this->index(self, i); }))
3271 return false;
3272 }
3273 return true;
3274}
3275
3276void ListPBase::writeOut(const DomItem &self, OutWriter &ow, bool compact) const
3277{
3278 const auto fLoc = FileLocations::treeOf(self);
3279 ow.writeRegion(fLoc, LeftBracketRegion);
3280 bool first = true;
3281 index_type len = index_type(m_pList.size());
3282 for (index_type i = 0; i < len; ++i) {
3283 if (first)
3284 first = false;
3285 else
3286 ow.writeRegion(fLoc, CommaTokenRegion).ensureSpace();
3287 if (!compact)
3288 ow.ensureNewline(1);
3289 DomItem el = index(self, i);
3290 el.writeOut(ow);
3291 }
3292 if (!compact && !first)
3293 ow.newline();
3294 ow.writeRegion(fLoc, RightBracketRegion);
3295}
3296
3298{
3299 return m_scope;
3300}
3301void ScriptElement::setSemanticScope(const QQmlJSScope::ConstPtr &scope)
3302{
3303 m_scope = scope;
3304}
3305
3306/*!
3307 \internal
3308 \brief Returns a pointer to the virtual base for virtual method calls.
3309
3310 A helper to call virtual methods without having to call std::visit(...).
3311 */
3313{
3314 if (!m_data)
3315 return nullptr;
3316
3317 return std::visit(
3318 [](auto &&e) {
3319 // std::reinterpret_pointer_cast does not exist on qnx it seems...
3320 return std::shared_ptr<ScriptElement>(
3321 e, static_cast<ScriptElement *>(e.get()));
3322 },
3323 *m_data);
3324}
3325
3326std::shared_ptr<ExternalOwningItem> getFileItemOwner(const DomItem &fileItem)
3327{
3328 switch (fileItem.internalKind()) {
3329 case DomType::JsFile:
3330 return fileItem.ownerAs<JsFile>();
3331 case DomType::QmlFile:
3332 return fileItem.ownerAs<QmlFile>();
3333 default:
3334 Q_UNREACHABLE_RETURN({});
3335 }
3336}
3337
3338} // end namespace Dom
3339} // end namespace QQmlJS
3340
3341QT_END_NAMESPACE
3342
3343#include "moc_qqmldomitem_p.cpp"
Path addObject(const QmlObject &object, QmlObject **oPtr=nullptr)
DomKind domKind() const override
ConstantData(const Path &pathFromOwner, const QCborValue &value, Options options=Options::MapIsMap)
quintptr id() const override
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
static CppTypeInfo fromString(QStringView target, const ErrorHandler &h=nullptr)
virtual DomItem containingObject(const DomItem &self) const
virtual void writeOut(const DomItem &self, OutWriter &lw) const
virtual QString canonicalFilePath(const DomItem &self) const
virtual void dump(const DomItem &, const Sink &sink, int indent, FilterT filter) const
virtual void updatePathFromOwner(const Path &newPath)
Path canonicalPath(const DomItem &self) const override
DomElement(const Path &pathFromOwner=Path())
DomItem containingObject(const DomItem &self) const override
A value type that references any element of the Dom.
DomItem top() const
DomItem goUp(int) const
QDateTime createdAt() const
bool resolve(const Path &path, Visitor visitor, const ErrorHandler &errorHandler, ResolveOptions options=ResolveOption::None, const Path &fullPath=Path(), QList< Path > *visitedRefs=nullptr) const
std::shared_ptr< OwningItem > owningItemPtr() const
DomItem operator[](const QString &component) const
QString toString() const
void writeOutPost(OutWriter &lw) const
bool visitTree(const Path &basePath, ChildrenVisitor visitor, VisitOptions options=VisitOption::Default, ChildrenVisitor openingVisitor=emptyChildrenVisitor, ChildrenVisitor closingVisitor=emptyChildrenVisitor, const FieldFilter &filter=FieldFilter::noFilter()) const
Visits recursively all the children of this item using the given visitors.
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
DomItem containingFile() const
DomItem filterUp(function_ref< bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const
DomItem scope(FilterUpOptions options=FilterUpOptions::ReturnOuter) const
bool visitLookup1(const QString &symbolName, function_ref< bool(const DomItem &)> visitor, LookupOptions=LookupOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem get(const ErrorHandler &h=nullptr, QList< Path > *visitedRefs=nullptr) const
static ErrorGroups myErrors()
DomItem operator[](const char16_t *component) const
bool visitUp(function_ref< bool(const DomItem &)> visitor) const
Let the visitor visit the Dom Tree hierarchy of this DomItem.
index_type indexes() const
bool hasAnnotations() const
bool iterateSubOwners(function_ref< bool(const DomItem &owner)> visitor) const
MutableDomItem makeCopy(CopyOption option=CopyOption::EnvConnected) const
bool iterateErrors(function_ref< bool(const DomItem &, const ErrorMessage &)> visitor, bool iterate, Path inPath=Path()) const
DomItem proceedToScope(const ErrorHandler &h=nullptr, QList< Path > *visitedRefs=nullptr) const
Dereference DomItems pointing to other DomItems.
QList< DomItem > values() const
DomItem universe() const
bool iterateDirectSubpaths(DirectVisitor v) const
DomItem container() const
void clearErrors(const ErrorGroups &groups=ErrorGroups({}), bool iterate=true) const
quintptr id() const
QList< QString > fields() const
DomItem globalScope() const
DomItem(const std::shared_ptr< DomEnvironment > &)
bool visitScopeChain(function_ref< bool(const DomItem &)> visitor, LookupOptions=LookupOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
Let the visitor visit the QML scope hierarchy of this DomItem.
std::shared_ptr< DomTop > topPtr() const
QCborValue value() const
QDateTime frozenAt() const
DomItem subListItem(const List &list) const
DomItem component(GoTo option=GoTo::Strict) const
bool visitLookup(const QString &symbolName, function_ref< bool(const DomItem &)> visitor, LookupType type=LookupType::Symbol, LookupOptions=LookupOption::Normal, const ErrorHandler &errorHandler=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
void writeOutPre(OutWriter &lw) const
DomItem lookupFirst(const QString &symbolName, LookupType type=LookupType::Symbol, LookupOptions=LookupOption::Normal, const ErrorHandler &errorHandler=nullptr) const
bool visitIndexes(function_ref< bool(const DomItem &)> visitor) const
DomItem fileObject(GoTo option=GoTo::Strict) const
bool visitKeys(function_ref< bool(const QString &, const DomItem &)> visitor) const
bool visitStaticTypePrototypeChains(function_ref< bool(const DomItem &)> visitor, VisitPrototypesOptions options=VisitPrototypesOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem::visitStaticTypePrototypeChains.
QQmlJSScope::ConstPtr semanticScope() const
bool writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const
bool commitToBase(const std::shared_ptr< DomEnvironment > &validPtr=nullptr) const
FileWriter::Status dump(const QString &path, function_ref< bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter=noFilter, int nBackups=2, int indent=0, FileWriter *fw=nullptr) const
QList< DomItem > lookup(const QString &symbolName, LookupType type=LookupType::Symbol, LookupOptions=LookupOption::Normal, const ErrorHandler &errorHandler=nullptr) const
void dump(const Sink &, int indent=0, function_ref< bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter=noFilter) const
friend QMLDOM_EXPORT bool operator==(const DomItem &, const DomItem &)
QSet< QString > propertyInfoNames() const
static ErrorGroups myResolveErrors()
bool visitDirectAccessibleScopes(function_ref< bool(const DomItem &)> visitor, VisitPrototypesOptions options=VisitPrototypesOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem environment() const
bool isCanonicalChild(const DomItem &child) const
DomItem operator[](QStringView component) const
DomItem rootQmlObject(GoTo option=GoTo::Strict) const
DomItem directParent() const
Path canonicalPath() const
QStringList sortedKeys() const
void addError(ErrorMessage &&msg) const
DomItem containingObject() const
DomItem containingScriptExpression() const
QList< DomItem > getAll(const ErrorHandler &h=nullptr, QList< Path > *visitedRefs=nullptr) const
bool visitPrototypeChain(function_ref< bool(const DomItem &)> visitor, VisitPrototypesOptions options=VisitPrototypesOption::Normal, const ErrorHandler &h=nullptr, QSet< quintptr > *visited=nullptr, QList< Path > *visitedRefs=nullptr) const
DomItem index(index_type) const
DomItem subReferencesItem(const PathEls::PathComponent &c, const QList< Path > &paths) const
bool visitLocalSymbolsNamed(const QString &name, function_ref< bool(const DomItem &)> visitor) const
DomItem subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const
void dumpPtr(const Sink &sink) const
InternalKind internalKind() const
DomItem path(const QString &p, const ErrorHandler &h=&defaultErrorHandler) const
bool isOwningItem() const
DomItem owner() const
The owner of an element, for an qmlObject this is the containing qml file.
ErrorHandler errorHandler() const
DomItem operator[](const Path &path) const
static DomItem empty
QQmlJSScope::ConstPtr nearestSemanticScope() const
QString canonicalFilePath() const
void writeOut(OutWriter &lw) const
bool writeOut(const QString &path, int nBackups=2, const LineWriterOptions &opt=LineWriterOptions(), FileWriter *fw=nullptr, WriteOutChecks extraChecks=WriteOutCheck::Default) const
Path pathFromOwner() const
DomItem qmlObject(GoTo option=GoTo::Strict, FilterUpOptions options=FilterUpOptions::ReturnOuter) const
Returns the QmlObject that this belongs to.
DomItem path(QStringView p, const ErrorHandler &h=&defaultErrorHandler) const
DomItem key(const QString &name) const
PropertyInfo propertyInfoWithName(const QString &name) const
QSet< QString > keys() const
DomItem field(QStringView name) const
DomItem goToFile(const QString &filePath) const
QDateTime lastDataUpdateAt() const
DomItem(const std::shared_ptr< DomUniverse > &)
static ErrorGroup domErrorGroup
void dump(const DomItem &, const Sink &s, int indent, function_ref< bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const override
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
Path pathFromOwner() const override
Path canonicalPath(const DomItem &self) const override
DomItem containingObject(const DomItem &self) const override
convenience macro creating a new ErrorGroup and registering its groupId as translatable string
Represents a set of tags grouping a set of related error messages.
ErrorMessage warning(const Dumper &message) const
ErrorMessage error(const Dumper &message) const
Represents an error message connected to the dom.
ErrorMessage handle(const ErrorHandler &errorHandler=nullptr)
ErrorMessage & withItem(const DomItem &)
static FieldFilter compareNoCommentsFilter()
FileToLoad(const std::weak_ptr< DomEnvironment > &environment, const QString &canonicalPath, const QString &logicalPath, const std::optional< InMemoryContents > &content)
const LineWriterOptions & options() const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const override
void writeOut(const DomItem &self, OutWriter &ow, bool compact) const
void writeOut(const DomItem &self, OutWriter &ow, bool compact) const
std::function< bool(const DomItem &, function_ref< bool(index_type, function_ref< DomItem()>)>)> IteratorFunction
List(const Path &pathFromOwner, const LookupFunction &lookup, const Length &length, const IteratorFunction &iterator, const QString &elType)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
void dump(const DomItem &, const Sink &s, int indent, function_ref< bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>) const override
std::function< DomItem(const DomItem &, index_type)> LookupFunction
quintptr id() const override
index_type indexes(const DomItem &self) const override
DomItem index(const DomItem &self, index_type index) const override
std::function< index_type(const DomItem &)> Length
std::function< DomItem(const DomItem &, QString)> LookupFunction
Map(const Path &pathFromOwner, const LookupFunction &lookup, const Keys &keys, const QString &targetType)
std::function< QSet< QString >(const DomItem &)> Keys
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
QSet< QString > const keys(const DomItem &self) const override
quintptr id() const override
DomItem key(const DomItem &self, const QString &name) const override
MutableDomItem addChild(QmlObject child)
MutableDomItem addPropertyDef(const PropertyDefinition &propertyDef, AddOption option=AddOption::Overwrite)
MutableDomItem addMethod(const MethodInfo &functionDef, AddOption option=AddOption::Overwrite)
void addError(ErrorMessage &&msg)
MutableDomItem path(const Path &p)
MutableDomItem addBinding(Binding binding, AddOption option=AddOption::Overwrite)
A DomItem that owns other DomItems and is managed through a shared pointer.
QDateTime createdAt() const
virtual bool iterateSubOwners(const DomItem &self, function_ref< bool(const DomItem &owner)> visitor)
virtual int revision() const
DomItem containingObject(const DomItem &self) const override
bool iterateErrors(const DomItem &self, function_ref< bool(const DomItem &source, const ErrorMessage &msg)> visitor, const Path &inPath=Path())
virtual void addError(const DomItem &self, ErrorMessage &&msg)
QDateTime frozenAt() const
void addErrorLocal(ErrorMessage &&msg)
virtual QDateTime lastDataUpdateAt() const
void clearErrors(const ErrorGroups &groups=ErrorGroups({}))
OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
virtual bool frozen() const
virtual void refreshedDataAt(QDateTime tNew)
OwningItem(int derivedFrom=0)
OwningItem(const OwningItem &o)
PathCurrent headCurrent() const
Path dropTail(int n=1) const
Path withPath(const Path &toAdd, bool avoidToAddAsBase=false) const
Returns a copy of this with toAdd appended to it.
Source split() const
Splits the path at the last field, root or current Component.
PathEls::Kind Kind
Path operator[](int i) const
Path mid(int offset, int length) const
Kind headKind() const
std::function< bool(const DomItem &)> headFilter() const
PathRoot headRoot() const
MutableDomItem addBinding(MutableDomItem &self, const Binding &binding, AddOption option)
MutableDomItem addChild(MutableDomItem &self, const QmlObject &child)
static constexpr DomType kindValue
MutableDomItem addPropertyDef(MutableDomItem &self, const PropertyDefinition &propertyDef, AddOption option)
MutableDomItem addMethod(MutableDomItem &self, const MethodInfo &functionDef, AddOption option)
static bool addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry, AddOption addOption=AddOption::KeepExisting)
QList< QString > fields(const DomItem &self) const override
quintptr id() const override
static constexpr DomType kindValue
DomItem index(const DomItem &, index_type) const override
DomItem field(const DomItem &self, QStringView name) const override
DomItem get(const DomItem &self, const ErrorHandler &h=nullptr, QList< Path > *visitedRefs=nullptr) const
QList< DomItem > getAll(const DomItem &self, const ErrorHandler &h=nullptr, QList< Path > *visitedRefs=nullptr) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
Reference(const Path &referredObject=Path(), const Path &pathFromOwner=Path(), const SourceLocation &loc=SourceLocation())
DomItem key(const DomItem &, const QString &) const override
Use this to contain any script element.
ScriptElement::PointerType< ScriptElement > base() const
Returns a pointer to the virtual base for virtual method calls.
Provides entities to maintain mappings between elements and their location in a file.
QMLDOM_EXPORT bool domTypeIsContainer(DomType k)
static QMap< QString, LookupType > stringToLookupTypeMap()
constexpr bool domTypeIsOwningItem(DomType)
static std::optional< LookupType > lookupTypeFromString(const QString &expectedType)
static bool visitForLookupType(const DomItem &el, LookupType lookupType, function_ref< bool(const DomItem &)> visitor)
static DomItem rootFromContext(const DomItem &root, PathRoot contextId)
QMLDOM_EXPORT QMap< DomKind, QString > domKindToStringMap()
std::shared_ptr< ExternalOwningItem > getFileItemOwner(const DomItem &fileItem)
static bool visitQualifiedNameLookup(const DomItem &newIt, const QStringList &subpath, function_ref< bool(const DomItem &)> visitor, LookupType lookupType, const ErrorHandler &errorHandler, QList< Path > *visitedRefs)
bool operator!=(const DomItem &o1, const DomItem &o2)
QMLDOM_EXPORT bool domTypeIsExternalItem(DomType k)
QMLDOM_EXPORT QMap< DomType, QString > domTypeToStringMap()
QMLDOM_EXPORT bool domTypeIsScope(DomType k)
static LookupOptions resolveLookupOptions(const PathCurrent &current, const DomItem &it)
QMLDOM_EXPORT QString domTypeToString(DomType k)
QMLDOM_EXPORT QString domKindToString(DomKind k)
bool operator!=(const Path &lhs, const Path &rhs)
std::function< void(const ErrorMessage &)> ErrorHandler
QMLDOM_EXPORT bool domTypeIsTopItem(DomType k)
QMLDOM_EXPORT void defaultErrorHandler(const ErrorMessage &)
Calls the default error handler (by default errorToQDebug).
static bool visitPrototypeIndex(QList< DomItem > &toDo, const DomItem &current, const DomItem &derivedFromPrototype, const ErrorHandler &h, QList< Path > *visitedRefs, VisitPrototypesOptions options, const DomItem &prototype)
bool operator==(const Path &lhs, const Path &rhs)
static DomItem resolveReference(const DomItem &it, const Path &refRef, QList< Path > *visitedRefs, const ErrorHandler &errorHandler, const ErrorGroups &errorGroup)
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
#define NewErrorGroup(name)
A common base class for all the script elements.
void setSemanticScope(const QQmlJSScope::ConstPtr &scope)
QQmlJSScope::ConstPtr semanticScope()