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.dvValue(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.dvValue(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 canonicalPath,
368 path,
369 InMemoryContents{ code },
370 };
371}
372
373FileToLoad FileToLoad::fromFileSystem(const std::weak_ptr<DomEnvironment> &environment,
374 const QString &path)
375{
376 // make the path canonical so the file content can be loaded from it later
377 const QString canonicalPath = QFileInfo(path).canonicalFilePath();
378 return {
379 environment,
380 canonicalPath,
381 path,
382 std::nullopt,
383 };
384}
385
388
390{
391 static ErrorGroups res = {{domErrorGroup}};
392 return res;
393}
394
396{
397 static ErrorGroups res = {{domErrorGroup, NewErrorGroup("Resolve")}};
398 return res;
399}
400
402{
403 Path res = visitEl([this](auto &&el) { return el->canonicalPath(*this); });
404 if (!(!res || res.headKind() == Path::Kind::Root)) {
405 qCWarning(domLog) << "non anchored canonical path:" << res.toString();
406 Q_ASSERT(false);
407 }
408 return res;
409
410}
411
413{
414 return visitEl([this](auto &&el) { return el->containingObject(*this); });
415}
416
417/*!
418 \internal
419 \brief Returns the QmlObject that this belongs to.
420
421 qmlObject() might also return the object of a component if GoTo:MostLikely is used.
422 */
423DomItem DomItem::qmlObject(GoTo options, FilterUpOptions filterOptions) const
424{
425 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlObject; },
426 filterOptions))
427 return res;
428 if (options == GoTo::MostLikely) {
429 if (DomItem comp = component(options))
430 return comp.field(Fields::objects).index(0);
431 }
432 return DomItem();
433}
434
435DomItem DomItem::fileObject(GoTo options) const
436{
437 DomItem res = *this;
439 if (k == DomType::List || k == DomType::Map) {
440 res = res.containingObject();
441 k = res.internalKind();
442 }
443 if (k == DomType::ExternalItemInfo || (options == GoTo::MostLikely && k == DomType::ExternalItemPair))
444 return field(Fields::currentItem);
445 res = owner();
446 k = res.internalKind();
447 while (k != DomType::Empty) {
449 || k == DomType::JsFile)
450 break;
451 res = res.containingObject();
452 res = res.owner();
453 k = res.internalKind();
454 }
455 return res;
456}
457
458DomItem DomItem::rootQmlObject(GoTo options) const
459{
460 return qmlObject(options, FilterUpOptions::ReturnInner);
461}
462
464{
465 Path path = pathFromOwner();
466 if (!path)
467 path = canonicalPath();
468 Source s = path.split();
469 if (s.pathFromSource.length() > 1)
470 return containingObject().path(s.pathFromSource.dropTail());
471 return containingObject();
472}
473
475{
477 return *this;
479 if (shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
480 return env.copy(envPtr->ensureGlobalScopeWithName(env, envPtr->globalScopeName())->current,
481 Path());
482 }
483 return DomItem();
484}
485
486/*!
487 \internal
488 \brief The owner of an element, for an qmlObject this is the containing qml file.
489 */
491{
492 if (domTypeIsOwningItem(m_kind) || m_kind == DomType::Empty)
493 return *this;
494 return std::visit([this](auto &&el) {
495 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
496 return DomItem();
497 else
498 return DomItem(this->m_top, el, this->m_ownerPath, el.get());
499 }, m_owner);
500}
501
503{
504 if (domTypeIsTopItem(m_kind) || m_kind == DomType::Empty)
505 return *this;
506 return std::visit([](auto &&el) {
507 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
508 return DomItem();
509 else
510 return DomItem(el, el, Path(), el.get());
511 }, m_top);
512}
513
515{
516 DomItem res = top();
518 return res;
519 return DomItem(); // we are in the universe, and cannot go back to the environment...
520}
521
523{
524 DomItem res = top();
526 return res;
528 return res.field(Fields::universe);
529 return DomItem(); // we should be in an empty DomItem already...
530}
531
532/*!
533 \internal
534 Shorthand to obtain the ScriptExpression DomItem, in which this DomItem is defined.
535 Returns an empty DomItem if the item is not defined inside a ScriptExpression.
536 \sa goToFile()
537 */
539{
540 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::ScriptExpression; },
541 FilterUpOptions::ReturnOuter))
542 return res;
543 return DomItem();
544}
545
546/*!
547 \internal
548 Shorthand to obtain the QmlFile DomItem, in which this DomItem is defined.
549 Returns an empty DomItem if the item is not defined in a QML file.
550 \sa goToFile()
551 */
553{
554 if (DomItem res = filterUp([](DomType k, const DomItem &) { return k == DomType::QmlFile; },
555 FilterUpOptions::ReturnOuter))
556 return res;
557 return DomItem();
558}
559
560/*!
561 \internal
562 Shorthand to obtain the QmlFile DomItem from a canonicalPath.
563 \sa containingFile()
564 */
565DomItem DomItem::goToFile(const QString &canonicalPath) const
566{
567 Q_UNUSED(canonicalPath);
568 DomItem file =
569 top().field(Fields::qmlFileWithPath).key(canonicalPath).field(Fields::currentItem);
570 return file;
571}
572
573/*!
574 \internal
575 In the DomItem hierarchy, go \c n levels up.
576 */
577DomItem DomItem::goUp(int n) const
578{
579 Path path = canonicalPath();
580 // first entry of path is usually top(), and you cannot go up from top().
581 if (path.length() < n + 1)
582 return DomItem();
583
584 DomItem parent = top().path(path.dropTail(n));
585 return parent;
586}
587
588/*!
589 \internal
590 In the DomItem hierarchy, go 1 level up to get the direct parent.
591 */
593{
594 return goUp(1);
595}
596
597/*!
598\internal
599Finds the first element in the DomItem hierarchy that satisfies filter.
600Use options to set the search direction, see also \l{FilterUpOptions}.
601*/
602DomItem DomItem::filterUp(function_ref<bool(DomType k, const DomItem &)> filter, FilterUpOptions options) const
603{
604 if (options == FilterUpOptions::ReturnOuter && filter(internalKind(), *this)) {
605 return *this;
606 }
607
608 switch (options) {
611 for (DomItem current = *this, previous = DomItem(); current;
612 previous = current, current = current.directParent()) {
613 if (filter(current.internalKind(), current)) {
614 if (options != FilterUpOptions::ReturnOuterNoSelf || current != *this)
615 return current;
616 }
617 }
618 break;
619 }
621 DomItem current = top();
622 for (const Path &currentPath : canonicalPath()) {
623 current = current.path(currentPath);
624 if (filter(current.internalKind(), current))
625 return current;
626 }
627 break;
628 }
629
630 return DomItem();
631}
632
634{
635 DomItem res = filterUp([](DomType, const DomItem &el) { return el.isScope(); }, options);
636 return res;
637}
638
640{
641 QQmlJSScope::ConstPtr scope;
642 visitUp([&scope](const DomItem &item) {
643 scope = item.semanticScope();
644 return !scope; // stop when scope was true
645 });
646 return scope;
647}
648
650{
651 QQmlJSScope::ConstPtr scope = std::visit(
652 [](auto &&e) -> QQmlJSScope::ConstPtr {
653 using T = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
654 if constexpr (std::is_same_v<T, const QmlObject *>) {
655 return e->semanticScope();
656 } else if constexpr (std::is_same_v<T, const QmlComponent *>) {
657 return e->semanticScope();
658 } else if constexpr (std::is_same_v<T, const QmltypesComponent *>) {
659 return e->semanticScope();
660 } else if constexpr (std::is_same_v<T, SimpleObjectWrap>) {
661 if (const MethodInfo *mi = e->template as<MethodInfo>()) {
662 return mi->semanticScope();
663 }
664 if (const auto *propertyDefinition = e->template as<PropertyDefinition>()) {
665 return propertyDefinition->semanticScope();
666 }
667 } else if constexpr (std::is_same_v<T, ScriptElementDomWrapper>) {
668 return e.element().base()->semanticScope();
669 }
670 return {};
671 },
672 m_element);
673 return scope;
674}
675
676DomItem DomItem::get(const ErrorHandler &h, QList<Path> *visitedRefs) const
677{
678 if (const Reference *refPtr = as<Reference>())
679 return refPtr->get(*this, h, visitedRefs);
680 return DomItem();
681}
682
683QList<DomItem> DomItem::getAll(const ErrorHandler &h, QList<Path> *visitedRefs) const
684{
685 if (const Reference *refPtr = as<Reference>())
686 return refPtr->getAll(*this, h, visitedRefs);
687 return {};
688}
689
690PropertyInfo DomItem::propertyInfoWithName(const QString &name) const
691{
692 PropertyInfo pInfo;
693 visitPrototypeChain([&pInfo, name](const DomItem &obj) {
694 return obj.visitLocalSymbolsNamed(name, [&pInfo, name](const DomItem &el) {
695 switch (el.internalKind()) {
696 case DomType::Binding:
697 pInfo.bindings.append(el);
698 break;
699 case DomType::PropertyDefinition:
700 pInfo.propertyDefs.append(el);
701 break;
702 default:
703 break;
704 }
705 return true;
706 });
707 });
708 return pInfo;
709}
710
712{
713 QSet<QString> res;
714 visitPrototypeChain([&res](const DomItem &obj) {
715 res += obj.propertyDefs().keys();
716 res += obj.bindings().keys();
717 return true;
718 });
719 return res;
720}
721
722DomItem DomItem::component(GoTo options) const
723{
724 if (DomItem res = filterUp(
725 [](DomType kind, const DomItem &) {
726 return kind == DomType::QmlComponent || kind == DomType::QmltypesComponent
727 || kind == DomType::GlobalComponent;
728 },
729 FilterUpOptions::ReturnInner))
730 return res;
731 if (options == GoTo::MostLikely) {
732 DomItem item = *this;
733 DomType kind = item.internalKind();
734 if (kind == DomType::List || kind == DomType::Map) {
735 item = item.containingObject();
736 kind = item.internalKind();
737 }
738 switch (kind) {
739 case DomType::ExternalItemPair:
740 case DomType::ExternalItemInfo:
741 item = fileObject(options);
742 Q_FALLTHROUGH();
743 case DomType::QmlFile:
744 return item.field(Fields::components).key(QString()).index(0);
745 default:
746 break;
747 }
748 }
749 return DomItem();
750}
751
756
758{
759 static QMap<LookupType, QString> map = []() {
760 QMetaEnum metaEnum = QMetaEnum::fromType<LookupType>();
761 QMap<LookupType, QString> res;
762 for (int i = 0; i < metaEnum.keyCount(); ++i) {
763 res[LookupType(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
764 }
765 return res;
766 }();
767 return map;
768}
769
770bool DomItem::resolve(const Path &path, DomItem::Visitor visitor, const ErrorHandler &errorHandler,
771 ResolveOptions options, const Path &fullPath, QList<Path> *visitedRefs) const
772{
773 QList<Path> vRefs;
774 Path fPath = fullPath;
775 if (fullPath.length() == 0)
776 fPath = path;
777 if (path.length()==0)
778 return visitor(fPath, *this);
779 QList<QSet<quintptr>> visited(path.length() + 1);
780 Path myPath = path;
781 QVector<ResolveToDo> toDos(1); // invariant: always increase pathIndex to guarantee end even with only partial visited match
782 if (path.headKind() == Path::Kind::Root) {
783 DomItem root = *this;
784 PathRoot contextId = path.headRoot();
785 switch (contextId) {
787 root = root.environment().field(Fields::moduleIndexWithUri);
788 break;
789 case PathRoot::Cpp:
790 root = root.environment()[Fields::qmltypesFileWithPath];
791 break;
792 case PathRoot::Libs:
793 root = root.environment()[Fields::plugins];
794 break;
795 case PathRoot::Top:
796 root = root.top();
797 break;
798 case PathRoot::Env:
799 root = root.environment();
800 break;
802 root = root.environment()[u"universe"];
803 break;
804 case PathRoot::Other:
805 myResolveErrors().error(tr("Root context %1 is not known").arg(path.headName())).handle(errorHandler);
806 return false;
807 }
808 toDos[0] = {std::move(root), 1};
809 } else {
810 toDos[0] = {*this, 0};
811 }
812 while (!toDos.isEmpty()) {
813 const ResolveToDo toDo = toDos.takeLast();
814 {
815 auto idNow = toDo.item.id();
816 if (idNow == quintptr(0) && toDo.item == *this)
817 idNow = quintptr(this);
818 if (idNow != quintptr(0) && visited[0].contains(idNow))
819 continue;
820 }
821 int iPath = toDo.pathIndex;
822 DomItem it = toDo.item;
823 bool branchExhausted = false;
824 while (iPath < path.length() && it && !branchExhausted) {
825 auto idNow = it.id();
826 if (idNow == quintptr() && toDo.item == *this)
827 idNow = quintptr(this);
828 if (idNow != quintptr(0)) {
829 auto vPair = std::make_pair(idNow, iPath);
830 if (visited[vPair.second].contains(vPair.first))
831 break;
832 visited[vPair.second].insert(vPair.first);
833 }
834 if (options & ResolveOption::TraceVisit && !visitor(path.mid(0,iPath), it))
835 return false;
836 auto cNow = path[iPath++];
837 switch (cNow.headKind()) {
838 case Path::Kind::Key:
839 it = it.key(cNow.headName());
840 break;
842 if (cNow.checkHeadName(Fields::get) && it.internalKind() == DomType::Reference) {
843 Path toResolve = it.as<Reference>()->referredObjectPath;
844 Path refRef = it.canonicalPath();
845 if (visitedRefs == nullptr) {
846 visitedRefs = &vRefs;
847 }
848 if (visitedRefs->contains(refRef)) {
850 .error([visitedRefs, refRef](const Sink &sink) {
851 const QString msg = tr("Circular reference:") + QLatin1Char('\n');
852 sink(QStringView{msg});
853 for (const Path &vPath : *visitedRefs) {
854 sink(u" ");
855 vPath.dump(sink);
856 sink(u" >\n");
857 }
858 refRef.dump(sink);
859 })
860 .handle(errorHandler);
861 it = DomItem();
862 } else {
863 visitedRefs->append(refRef);
864 DomItem resolveRes;
865 it.resolve(
866 toResolve,
867 [&resolveRes](Path, const DomItem &r) {
868 resolveRes = r;
869 return false;
870 },
871 errorHandler, ResolveOption::None, toResolve, visitedRefs);
872 it = resolveRes;
873 }
874 } else {
875 it = it.field(cNow.headName()); // avoid instantiation of QString?
876 }
877 break;
879 it = it.index(cNow.headIndex());
880 break;
882 {
883 // immediate expansion, might use extra memory, but simplifies code (no suspended evaluation)
884 Path toFind;
885 do {
886 if (iPath >= path.length()) {
887 myResolveErrors().warning(tr("Resolve with path ending with empty path, matches nothing."))
888 .handle(errorHandler);
889 branchExhausted = true; // allow and visit all?
890 break;
891 }
892 toFind = path[iPath++];
893 } while (toFind.headKind() == Path::Kind::Empty);
894 QVector<Path::Kind> validFind({Path::Kind::Key, Path::Kind::Field, Path::Kind::Field, Path::Kind::Index});
895 if (!validFind.contains(toFind.headKind())) {
896 myResolveErrors().error(tr("After an empty path only key, field or indexes are supported, not %1.").arg(toFind.toString()))
897 .handle(errorHandler);
898 branchExhausted = true; // allow and visit all?
899 return false;
900 }
901 if (!branchExhausted)
902 visitTree(
903 Path(),
904 [&toFind, &toDos, iPath](Path, const DomItem &item, bool) {
905 // avoid non directly attached?
906 DomItem newItem = item[toFind];
907 if (newItem)
908 toDos.append({ std::move(newItem), iPath });
909 return true;
910 },
911 VisitOption::VisitSelf | VisitOption::Recurse
912 | VisitOption::VisitAdopted | VisitOption::NoPath);
913 branchExhausted = true;
914 break;
915 }
916 case Path::Kind::Root:
917 myResolveErrors().error(tr("Root path is supported only at the beginning, and only once, found %1 at %2 in %3")
918 .arg(cNow.toString()).arg(iPath -1).arg(path.toString())).handle(errorHandler);
919 return false;
921 {
922 PathCurrent current = cNow.headCurrent();
923 switch (current) {
925 // todo
926 case PathCurrent::Obj:
927 if (domKind() != DomKind::Object)
928 it = it.containingObject();
929 break;
931 bool cont = it.visitPrototypeChain(
932 [&toDos, iPath](const DomItem &subEl) {
933 toDos.append({ subEl, iPath });
934 return true;
935 },
936 VisitPrototypesOption::Normal, errorHandler, nullptr,
937 visitedRefs); // avoid passing visitedRefs?
938 if (!cont)
939 return false;
940 branchExhausted = true;
941 break;
942 }
944 bool cont = it.visitScopeChain(
945 [&toDos, iPath](const DomItem &subEl) {
946 toDos.append({ subEl, iPath });
947 return true;
948 },
949 LookupOption::Normal, errorHandler);
950 if (!cont)
951 return false;
952 branchExhausted = true;
953 break;
954 }
956 it = it.component();
957 break;
959 case PathCurrent::Ids:
960 it = it.component().ids();
961 break;
963 it = it.component()[Fields::exports];
964 break;
967 case PathCurrent::Lookup: {
968 LookupOptions opt = LookupOption::Normal;
969 if (current == PathCurrent::Lookup) {
970 DomItem comp = it.component();
971 DomItem strict = comp.field(u"~strictLookup~");
972 if (!strict) {
973 DomItem env = it.environment();
974 strict = env.field(u"defaultStrictLookup");
975 }
976 if (strict && strict.value().toBool())
977 opt = opt | LookupOption::Strict;
978 } else if (current == PathCurrent::LookupStrict) {
979 opt = opt | LookupOption::Strict;
980 }
983 .error(tr("Javascript lookups not yet implemented"))
984 .handle(errorHandler);
985 return false;
986 }
987 // enter lookup
988 auto idNow = it.id();
989 if (idNow == quintptr(0) && toDo.item == *this)
990 idNow = quintptr(this);
991 if (idNow != quintptr(0)) {
992 auto vPair = std::make_pair(idNow, iPath);
993 if (visited[vPair.second].contains(vPair.first))
994 break;
995 visited[vPair.second].insert(vPair.first);
996 }
997 if (options & ResolveOption::TraceVisit && !visitor(path.mid(0, iPath), it))
998 return false;
999 if (iPath + 1 >= path.length()) {
1001 .error(tr("Premature end of path, expected a field specifying the "
1002 "type, and a key specifying the name to search after a "
1003 "lookup directive in %2")
1004 .arg(path.toString()))
1005 .handle(errorHandler);
1006 return false;
1007 }
1008 Path cNow = path[iPath++];
1009 if (cNow.headKind() != Path::Kind::Field) {
1011 .error(tr("Expected a key path specifying the type to search after "
1012 "a lookup directive, not %1 at component %2 of %3")
1013 .arg(cNow.toString())
1014 .arg(iPath)
1015 .arg(path.toString()))
1016 .handle(errorHandler);
1017 return false;
1018 }
1019 QString expectedType = cNow.headName();
1020 LookupType lookupType = LookupType::Symbol;
1021 {
1022 bool found = false;
1023 auto m = lookupTypeToStringMap();
1024 auto it = m.begin();
1025 auto end = m.end();
1026 while (it != end) {
1027 if (it.value().compare(expectedType, Qt::CaseInsensitive) == 0) {
1028 lookupType = it.key();
1029 found = true;
1030 }
1031 ++it;
1032 }
1033 if (!found) {
1034 QString types;
1035 it = lookupTypeToStringMap().begin();
1036 while (it != end) {
1037 if (!types.isEmpty())
1038 types += QLatin1String("', '");
1039 types += it.value();
1040 ++it;
1041 }
1043 .error(tr("Type for lookup was expected to be one of '%1', not "
1044 "%2")
1045 .arg(types, expectedType))
1046 .handle(errorHandler);
1047 return false;
1048 }
1049 }
1050 cNow = path[iPath++];
1051 if (cNow.headKind() != Path::Kind::Key) {
1053 .error(tr("Expected a key specifying the path to search after the "
1054 "@lookup directive and type, not %1 at component %2 of "
1055 "%3")
1056 .arg(cNow.toString())
1057 .arg(iPath)
1058 .arg(path.toString()))
1059 .handle(errorHandler);
1060 return false;
1061 }
1062 QString target = cNow.headName();
1063 if (target.isEmpty()) {
1065 .warning(tr("Path with empty lookup at component %1 of %2 will "
1066 "match nothing in %3.")
1067 .arg(iPath)
1068 .arg(path.toString())
1069 .arg(it.canonicalPath().toString()))
1070 .handle(errorHandler);
1071 return true;
1072 }
1073 it.visitLookup(
1074 target,
1075 [&toDos, iPath](const DomItem &subEl) {
1076 toDos.append({ subEl, iPath });
1077 return true;
1078 },
1079 lookupType, opt, errorHandler, &(visited[iPath]), visitedRefs);
1080 branchExhausted = true;
1081 break;
1082 }
1083 }
1084 break;
1085 }
1086 case Path::Kind::Any:
1087 visitTree(
1088 Path(),
1089 [&toDos, iPath](Path, const DomItem &item, bool) {
1090 toDos.append({ item, iPath });
1091 return true;
1092 },
1093 VisitOption::VisitSelf | VisitOption::Recurse | VisitOption::VisitAdopted);
1094 branchExhausted = true;
1095 break;
1096 case Path::Kind::Filter:
1097 if (cNow.headFilter() && !cNow.headFilter()(it))
1098 branchExhausted = true;
1099 break;
1100 }
1101 }
1102 // visit the resolved path
1103 if (!branchExhausted && iPath == path.length() && !visitor(fPath, it))
1104 return false;
1105 }
1106 return true;
1107}
1108
1109DomItem DomItem::path(const Path &p, const ErrorHandler &errorHandler) const
1110{
1111 if (!p)
1112 return *this;
1113 DomItem res;
1114 resolve(p, [&res](const Path &, const DomItem &it) {
1115 res = it;
1116 return false;
1117 }, errorHandler);
1118 return res;
1119}
1120
1121DomItem DomItem::path(const QString &p, const ErrorHandler &errorHandler) const
1122{
1123 return path(Path::fromString(p, errorHandler));
1124}
1125
1126DomItem DomItem::path(QStringView p, const ErrorHandler &errorHandler) const
1127{
1128 return path(Path::fromString(p, errorHandler));
1129}
1130
1132{
1133 return visitEl([this](auto &&el) { return el->fields(*this); });
1134}
1135
1136DomItem DomItem::field(QStringView name) const
1137{
1138 return visitEl([this, name](auto &&el) { return el->field(*this, name); });
1139}
1140
1142{
1143 return visitEl([this](auto &&el) { return el->indexes(*this); });
1144}
1145
1146DomItem DomItem::index(index_type i) const
1147{
1148 return visitEl([this, i](auto &&el) { return el->index(*this, i); });
1149}
1150
1151bool DomItem::visitIndexes(function_ref<bool(const DomItem &)> visitor) const
1152{
1153 // use iterateDirectSubpaths instead?
1154 int nIndexes = indexes();
1155 for (int i = 0; i < nIndexes; ++i) {
1156 DomItem v = index(i);
1157 if (!visitor(v))
1158 return false;
1159 }
1160 return true;
1161}
1162
1164{
1165 return visitEl([this](auto &&el) { return el->keys(*this); });
1166}
1167
1169{
1170 QSet<QString> ks = keys();
1171 QStringList sortedKs(ks.begin(), ks.end());
1172 std::sort(sortedKs.begin(), sortedKs.end());
1173 return sortedKs;
1174}
1175
1176DomItem DomItem::key(const QString &name) const
1177{
1178 return visitEl([this, name](auto &&el) { return el->key(*this, name); });
1179}
1180
1181bool DomItem::visitKeys(function_ref<bool(const QString &, const DomItem &)> visitor) const
1182{
1183 // use iterateDirectSubpaths instead?
1184 const QStringList keys = sortedKeys();
1185 for (const QString &k : keys) {
1186 DomItem v = key(k);
1187 if (!visitor(k, v))
1188 return false;
1189 }
1190 return true;
1191}
1192
1194{
1195 QList<DomItem> res;
1196 iterateDirectSubpaths([&res](const PathEls::PathComponent &, function_ref<DomItem()> item) {
1197 res.append(item());
1198 return true;
1199 });
1200 return res;
1201}
1202
1203void DomItem::writeOutPre(OutWriter &ow) const
1204{
1205 if (hasAnnotations()) {
1206 DomItem anns = field(Fields::annotations);
1207 for (const auto &ann : anns.values()) {
1208 if (ann.annotations().indexes() == 0) {
1209 ow.ensureNewline();
1210 ann.writeOut(ow);
1211 ow.ensureNewline();
1212 } else {
1213 DomItem annAnn = ann.annotations();
1214 Q_ASSERT_X(annAnn.indexes() == 1 && annAnn.index(0).name() == u"duplicate",
1215 "DomItem::writeOutPre", "Unexpected annotation of annotation");
1216 }
1217 }
1218 }
1219 ow.itemStart(*this);
1220}
1221
1222void DomItem::writeOut(OutWriter &ow) const
1223{
1224 writeOutPre(ow);
1225 visitEl([this, &ow](auto &&el) { el->writeOut(*this, ow); });
1227}
1228
1229void DomItem::writeOutPost(OutWriter &ow) const
1230{
1231 ow.itemEnd();
1232}
1233
1234DomItem::WriteOutCheckResult DomItem::performWriteOutChecks(const DomItem &reformatted,
1235 OutWriter &ow,
1236 WriteOutChecks extraChecks) const
1237{
1238 auto compare = [this](const DomItem &obj1, const DomItem &obj2, QStringView obj2Name,
1239 const FieldFilter &filter) {
1240 const auto diffList = domCompareStrList(obj1, obj2, filter, DomCompareStrList::AllDiffs);
1241 if (!diffList.isEmpty()) {
1242 qCWarning(writeOutLog).noquote().nospace()
1243 << obj2Name << " writeOut of " << this->canonicalFilePath() << " has changes:\n"
1244 << diffList.join(QString());
1245 return false;
1246 }
1247 return true;
1248 };
1249 auto checkStability = [&ow, this](const QString &expected, const DomItem &obj,
1250 QStringView objName) {
1251 LineWriter lw2([](QStringView) {}, ow.lineWriter.fileName(), ow.lineWriter.options());
1252 OutWriter ow2(lw2);
1253 ow2.indentNextlines = true;
1254 obj.writeOut(ow2);
1255 ow2.eof();
1256 if (ow2.writtenStr != expected) {
1257 qCWarning(writeOutLog).noquote().nospace()
1258 << objName << " non stable writeOut of " << this->canonicalFilePath() << ":"
1259 << lineDiff(expected, ow2.writtenStr, 2);
1260 return false;
1261 }
1262 return true;
1263 };
1264
1265 if (extraChecks
1268 std::shared_ptr<DomEnvironment> newEnvPtr = newEnv.ownerAs<DomEnvironment>();
1269 if (!newEnvPtr)
1270 return WriteOutCheckResult::Failed;
1271
1272 auto newFilePtr = std::make_shared<QmlFile>(canonicalFilePath(), ow.writtenStr);
1273 newEnvPtr->addQmlFile(newFilePtr, AddOption::Overwrite);
1274
1275 DomItem newFile = newEnv.copy(newFilePtr, Path());
1276 if (newFilePtr->isValid()) {
1278 newEnvPtr->populateFromQmlFile(newFile);
1279 if ((extraChecks & WriteOutCheck::ReparseCompare)
1280 && !compare(reformatted, newFile, u"reparsed",
1282 return WriteOutCheckResult::Failed;
1283 if ((extraChecks & WriteOutCheck::ReparseStable))
1284 checkStability(ow.writtenStr, newFile, u"reparsed");
1285 }
1286 } else {
1287 const auto iterateErrors = [&newFile](const Sink &s) {
1288 newFile.iterateErrors(
1289 [s](const DomItem &, const ErrorMessage &msg) {
1290 s(u"\n ");
1291 msg.dump(s);
1292 return true;
1293 },
1294 true);
1295 s(u"\n"); // extra empty line at the end...
1296 };
1297 qCWarning(writeOutLog).noquote().nospace()
1298 << "writeOut of " << canonicalFilePath()
1299 << " created invalid code:\n----------\n"
1300 << ow.writtenStr << "\n----------" << iterateErrors;
1301 return WriteOutCheckResult::Failed;
1302 }
1303 }
1304 return WriteOutCheckResult::Success;
1305}
1306
1307/*!
1308 \internal
1309 Performes WriteOut of the FileItem and verifies the consistency of the DOM structure.
1310
1311 OutWriter is essentially a visitor traversing the DOM structure, starting from
1312 the current item representing a FileItem.
1313 While traversing it might be saving some intermediate information, used later for restoring
1314 written out item. Restoration is needed to validate that the DOM structure of the written item
1315 has not changed.
1316*/
1317bool DomItem::writeOutForFile(OutWriter &ow, WriteOutChecks extraChecks) const
1318{
1319 ow.indentNextlines = true;
1320 auto currentFileItem = fileObject();
1321 writeOut(ow);
1322 ow.eof();
1323 WriteOutCheckResult result = WriteOutCheckResult::Success;
1324 if (extraChecks)
1325 result = performWriteOutChecks(currentFileItem, ow, extraChecks);
1326 return result == WriteOutCheckResult::Success ? bool(currentFileItem) : false;
1327}
1328
1329bool DomItem::writeOut(const QString &path, int nBackups, const LineWriterOptions &options,
1330 FileWriter *fw, WriteOutChecks extraChecks) const
1331{
1332 FileWriter localFw;
1333 if (!fw)
1334 fw = &localFw;
1335 auto status = fw->write(
1336 path,
1337 [this, path, &options, extraChecks](QTextStream &ts) {
1338 auto lw = createLineWriter([&ts](QStringView s) { ts << s; }, path, options);
1339 OutWriter ow(getFileItemOwner(fileObject()), *lw);
1340 return writeOutForFile(ow, extraChecks);
1341 },
1342 nBackups);
1343 switch (status) {
1344 case FileWriter::Status::DidWrite:
1345 case FileWriter::Status::SkippedEqual:
1346 return true;
1347 case FileWriter::Status::ShouldWrite:
1348 case FileWriter::Status::SkippedDueToFailure:
1349 qCWarning(writeOutLog) << "failure reformatting " << path;
1350 return false;
1351 default:
1352 qCWarning(writeOutLog) << "Unknown FileWriter::Status ";
1353 Q_ASSERT(false);
1354 return false;
1355 }
1356}
1357
1358bool DomItem::isCanonicalChild(const DomItem &item) const
1359{
1360 bool isChild = false;
1361 if (item.isOwningItem()) {
1363 } else {
1364 DomItem itemOw = item.owner();
1365 DomItem selfOw = owner();
1366 isChild = itemOw == selfOw && item.pathFromOwner().dropTail() == pathFromOwner();
1367 }
1368 return isChild;
1369}
1370
1372{
1373 bool hasAnnotations = false;
1374 DomType iKind = internalKind();
1375 switch (iKind) {
1376 case DomType::Id:
1377 if (const Id *myPtr = as<Id>())
1378 hasAnnotations = !myPtr->annotations.isEmpty();
1379 break;
1381 if (const PropertyDefinition *myPtr = as<PropertyDefinition>())
1382 hasAnnotations = !myPtr->annotations.isEmpty();
1383 break;
1385 if (const MethodInfo *myPtr = as<MethodInfo>())
1386 hasAnnotations = !myPtr->annotations.isEmpty();
1387 break;
1388 case DomType::QmlObject:
1389 if (const QmlObject *myPtr = as<QmlObject>())
1390 hasAnnotations = !myPtr->annotations().isEmpty();
1391 break;
1392 case DomType::Binding:
1393 if (const Binding *myPtr = as<Binding>())
1394 hasAnnotations = !myPtr->annotations().isEmpty();
1395 break;
1396 default:
1397 break;
1398 }
1399 return hasAnnotations;
1400}
1401
1402/*!
1403 \internal
1404 \brief Visits recursively all the children of this item using the given visitors.
1405
1406 First, the visitor is called and can continue or exit the visit by returning true or false.
1407
1408 Second, the openingVisitor is called and controls if the children of the current item needs to
1409 be visited or not by returning true or false. In either case, the visitation of all the other
1410 siblings is not affected. If both visitor and openingVisitor returned true, then the childrens of
1411 the current item will be recursively visited.
1412
1413 Finally, after all the children were visited by visitor and openingVisitor, the closingVisitor
1414 is called. Its return value is currently ignored.
1415
1416 Compared to the AST::Visitor*, openingVisitor and closingVisitor are called in the same order as
1417 the visit() and endVisit()-calls.
1418
1419 Filtering allows to not visit certain part of the trees, and is checked before(!) the lazy child
1420 is instantiated via its lambda. For example, visiting propertyInfos or defaultPropertyname takes
1421 a lot of time because it resolves and collects all properties inherited from base types, and
1422 might not even be relevant for the visitors.
1423 */
1424bool DomItem::visitTree(const Path &basePath, DomItem::ChildrenVisitor visitor,
1425 VisitOptions options, DomItem::ChildrenVisitor openingVisitor,
1426 DomItem::ChildrenVisitor closingVisitor, const FieldFilter &filter) const
1427{
1428 if (!*this)
1429 return true;
1430 if (options & VisitOption::VisitSelf && !visitor(basePath, *this, true))
1431 return false;
1432 if (options & VisitOption::VisitSelf && !openingVisitor(basePath, *this, true))
1433 return true;
1434 auto atEnd = qScopeGuard([closingVisitor, basePath, this, options]() {
1435 if (options & VisitOption::VisitSelf) {
1436 closingVisitor(basePath, *this, true);
1437 }
1438 });
1439 return iterateDirectSubpaths(
1440 [this, basePath, visitor, openingVisitor, closingVisitor, options,
1441 &filter](const PathEls::PathComponent &c, function_ref<DomItem()> itemF) {
1442 Path pNow;
1443 if (!(options & VisitOption::NoPath)) {
1444 pNow = basePath;
1445 pNow = pNow.withComponent(c);
1446 }
1447 if (!filter(*this, c, DomItem{}))
1448 return true;
1449 DomItem item = itemF();
1450 bool directChild = isCanonicalChild(item);
1451 if (!directChild && !(options & VisitOption::VisitAdopted))
1452 return true;
1453 if (!directChild || !(options & VisitOption::Recurse)) {
1454 if (!visitor(pNow, item, directChild))
1455 return false;
1456 // give an option to avoid calling open/close when not recursing?
1457 // calling it always allows close to do the reverse looping (children before
1458 // parent)
1459 if (!openingVisitor(pNow, item, directChild))
1460 return true;
1461 closingVisitor(pNow, item, directChild);
1462 } else {
1463 return item.visitTree(pNow, visitor, options | VisitOption::VisitSelf,
1464 openingVisitor, closingVisitor, filter);
1465 }
1466 return true;
1467 });
1468}
1469static bool visitPrototypeIndex(QList<DomItem> &toDo, const DomItem &current,
1470 const DomItem &derivedFromPrototype, const ErrorHandler &h,
1471 QList<Path> *visitedRefs, VisitPrototypesOptions options,
1472 const DomItem &prototype)
1473{
1474 Path elId = prototype.canonicalPath();
1475 if (visitedRefs->contains(elId))
1476 return true;
1477 else
1478 visitedRefs->append(elId);
1479 QList<DomItem> protos = prototype.getAll(h, visitedRefs);
1480 if (protos.isEmpty()) {
1481 if (std::shared_ptr<DomEnvironment> envPtr =
1482 derivedFromPrototype.environment().ownerAs<DomEnvironment>())
1483 if (!(envPtr->options() & DomEnvironment::Option::NoDependencies))
1484 derivedFromPrototype.myErrors()
1485 .warning(derivedFromPrototype.tr("could not resolve prototype %1 (%2)")
1486 .arg(current.canonicalPath().toString(),
1487 prototype.field(Fields::referredObjectPath)
1488 .value()
1489 .toString()))
1490 .withItem(derivedFromPrototype)
1491 .handle(h);
1492 } else {
1493 if (protos.size() > 1) {
1494 QStringList protoPaths;
1495 for (const DomItem &p : protos)
1496 protoPaths.append(p.canonicalPath().toString());
1497 derivedFromPrototype.myErrors()
1498 .warning(derivedFromPrototype
1499 .tr("Multiple definitions found, using first only, resolving "
1500 "prototype %1 (%2): %3")
1501 .arg(current.canonicalPath().toString(),
1502 prototype.field(Fields::referredObjectPath)
1503 .value()
1504 .toString(),
1505 protoPaths.join(QLatin1String(", "))))
1506 .withItem(derivedFromPrototype)
1507 .handle(h);
1508 }
1509 int nProtos = 1; // change to protos.length() to use all prototypes
1510 // (sloppier)
1511 for (int i = nProtos; i != 0;) {
1512 DomItem proto = protos.at(--i);
1513 if (proto.internalKind() == DomType::Export) {
1515 proto = proto.proceedToScope(h, visitedRefs);
1516 toDo.append(proto);
1517 } else if (proto.internalKind() == DomType::QmlObject
1519 toDo.append(proto);
1520 } else {
1521 derivedFromPrototype.myErrors()
1522 .warning(derivedFromPrototype.tr("Unexpected prototype type %1 (%2)")
1523 .arg(current.canonicalPath().toString(),
1524 prototype.field(Fields::referredObjectPath)
1525 .value()
1526 .toString()))
1527 .withItem(derivedFromPrototype)
1528 .handle(h);
1529 }
1530 }
1531 }
1532 return true;
1533}
1534
1535bool DomItem::visitPrototypeChain(function_ref<bool(const DomItem &)> visitor,
1536 VisitPrototypesOptions options, const ErrorHandler &h,
1537 QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1538{
1539 QSet<quintptr> visitedLocal;
1540 if (!visited)
1541 visited = &visitedLocal;
1542 QList<Path> refsLocal;
1543 if (!visitedRefs)
1544 visitedRefs = &refsLocal;
1545 bool shouldVisit = !(options & VisitPrototypesOption::SkipFirst);
1546 DomItem current = qmlObject();
1547 if (!current) {
1548 myErrors().warning(tr("Prototype chain called outside object")).withItem(*this).handle(h);
1549 return true;
1550 }
1551 QList<DomItem> toDo({ current });
1552 while (!toDo.isEmpty()) {
1553 current = toDo.takeLast();
1554 current = current.proceedToScope(h, visitedRefs);
1555 if (visited->contains(current.id())) {
1556 // to warn about circular dependencies a purely local visited trace is required
1557 // as common ancestors of unrelated objects are valid and should be skipped
1558 // so we do not to warn unless requested
1561 .warning(tr("Detected multiple visit of %1 visiting prototypes of %2")
1562 .arg(current.canonicalPath().toString(),
1563 canonicalPath().toString()))
1564 .withItem(*this)
1565 .handle(h);
1566 continue;
1567 }
1568 visited->insert(current.id());
1569 if (shouldVisit && !visitor(current))
1570 return false;
1571 shouldVisit = true;
1572 current.field(Fields::prototypes)
1573 .visitIndexes([&toDo, &current, this, &h, visitedRefs, options](const DomItem &el) {
1574 return visitPrototypeIndex(toDo, current, *this, h, visitedRefs, options, el);
1575 });
1576 }
1577 return true;
1578}
1579
1581 function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options,
1582 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1583{
1584 // these are the scopes one can access with the . operator from the current location
1585 // but currently not the attached types, which we should
1587 if (k == DomType::QmlObject)
1588 return visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1589 if (visited && id() != 0) {
1590 if (visited->contains(id()))
1591 return true;
1592 visited->insert(id());
1593 }
1594 if (k == DomType::Id || k == DomType::Reference || k == DomType::Export) {
1595 // we go to the scope if it is clearly defined
1596 DomItem v = proceedToScope(h, visitedRefs);
1598 return v.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1599 }
1600 if (k == DomType::Binding) {
1601 // from a binding we can get to its value if it is a object
1602 DomItem v = field(Fields::value);
1604 return v.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1605 }
1606 if (k == DomType::PropertyDefinition) {
1607 // from a property definition we go to the type stored in it
1608 DomItem t = field(Fields::type).proceedToScope(h, visitedRefs);
1610 return t.visitPrototypeChain(visitor, options, h, visited, visitedRefs);
1611 }
1612 if (!(options & VisitPrototypesOption::SkipFirst) && isScope() && !visitor(*this))
1613 return false;
1614 return true;
1615}
1616
1617/*!
1618 * \brief DomItem::visitStaticTypePrototypeChains
1619 * \param visitor
1620 * \param visitFirst
1621 * \param visited
1622 * \return
1623 *
1624 * visit the values JS reaches accessing a type directly: the values if it is a singleton or the
1625 * attached type
1626 */
1628 function_ref<bool(const DomItem &)> visitor, VisitPrototypesOptions options,
1629 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1630{
1631 QSet<quintptr> visitedLocal;
1632 if (!visited)
1633 visited = &visitedLocal;
1634 DomItem current = qmlObject();
1635 DomItem comp = current.component();
1636 if (comp.field(Fields::isSingleton).value().toBool(false)
1637 && !current.visitPrototypeChain(visitor, options, h, visited, visitedRefs))
1638 return false;
1639 if (DomItem attachedT = current.component().field(Fields::attachedType).field(Fields::get))
1640 if (!attachedT.visitPrototypeChain(
1641 visitor, options & ~VisitPrototypesOptions(VisitPrototypesOption::SkipFirst), h,
1642 visited, visitedRefs))
1643 return false;
1644 return true;
1645}
1646
1647/*!
1648 \brief Let the visitor visit the Dom Tree hierarchy of this DomItem.
1649 */
1650bool DomItem::visitUp(function_ref<bool(const DomItem &)> visitor) const
1651{
1653 while (p.length() > 0) {
1654 DomItem current = top().path(p);
1655 if (!visitor(current))
1656 return false;
1657 p = p.dropTail();
1658 }
1659 return true;
1660}
1661
1662/*!
1663 \brief Let the visitor visit the QML scope hierarchy of this DomItem.
1664 */
1666 function_ref<bool(const DomItem &)> visitor, LookupOptions options, const ErrorHandler &h,
1667 QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1668{
1669 QSet<quintptr> visitedLocal;
1670 if (!visited)
1671 visited = &visitedLocal;
1672 QList<Path> visitedRefsLocal;
1673 if (!visitedRefs)
1674 visitedRefs = &visitedRefsLocal;
1675 DomItem current = scope();
1676 if (!current) {
1677 myResolveErrors().warning(tr("Called visitScopeChain outside scopes")).handle(h);
1678 return true;
1679 }
1680 QList<DomItem> toDo { current };
1681 bool visitFirst = !(options & LookupOption::SkipFirstScope);
1682 bool visitCurrent = visitFirst;
1683 bool first = true;
1684 QSet<quintptr> alreadyAddedComponentMaps;
1685 while (!toDo.isEmpty()) {
1686 DomItem current = toDo.takeLast();
1687 if (visited->contains(current.id()))
1688 continue;
1689 visited->insert(current.id());
1690 if (visitCurrent && !visitor(current))
1691 return false;
1692 visitCurrent = true;
1693 switch (current.internalKind()) {
1694 case DomType::QmlObject: {
1695 if (!current.visitPrototypeChain(visitor, VisitPrototypesOption::SkipFirst, h, visited,
1696 visitedRefs))
1697 return false;
1698 DomItem root = current.rootQmlObject();
1699 if (root && root != current) {
1700 first = false;
1701 toDo.append(root);
1702 } else if (DomItem next = current.scope(
1703 FilterUpOptions::ReturnOuterNoSelf)) { // should be the component
1704 toDo.append(next);
1705 }
1706 } break;
1707 case DomType::ScriptExpression: // Js lexical scope
1708 first = false;
1710 toDo.append(next);
1711 break;
1712 case DomType::QmlComponent: { // ids/attached type
1713 if ((options & LookupOption::Strict) == 0) {
1714 if (DomItem comp = current.field(Fields::nextComponent))
1715 toDo.append(comp);
1716 }
1717 if (first && visitFirst && (options & LookupOption::VisitTopClassType)
1718 && *this == current) { // visit attached type if it is the top of the chain
1719 if (DomItem attachedT = current.field(Fields::attachedType).field(Fields::get))
1720 toDo.append(attachedT);
1721 }
1723 toDo.append(next);
1724
1725 DomItem owner = current.owner();
1726 Path pathToComponentMap = current.pathFromOwner().dropTail(2);
1727 DomItem componentMap = owner.path(pathToComponentMap);
1728 if (alreadyAddedComponentMaps.contains(componentMap.id()))
1729 break;
1730 alreadyAddedComponentMaps.insert(componentMap.id());
1731 const auto keys = componentMap.keys();
1732 for (const QString &x : keys) {
1733 DomItem componentList = componentMap.key(x);
1734 for (int i = 0; i < componentList.indexes(); ++i) {
1735 DomItem component = componentList.index(i);
1736 if (component != current && !visited->contains(component.id()))
1737 toDo.append(component);
1738 }
1739 }
1740 first = false;
1741 break;
1742 }
1743 case DomType::QmlFile: // subComponents, imported types
1744 if (DomItem iScope =
1745 current.field(Fields::importScope)) // treat file as a separate scope?
1746 toDo.append(iScope);
1747 first = false;
1748 break;
1749 case DomType::MethodInfo: // method arguments
1750 first = false;
1752 toDo.append(next);
1753 break;
1754 case DomType::ImportScope: // types
1755 first = false;
1756 if (auto globalC = globalScope().field(Fields::rootComponent))
1757 toDo.append(globalC);
1758 break;
1761 first = false;
1762 if (DomItem next = current.field(Fields::objects).index(0))
1763 toDo.append(next);
1764 break;
1766 first = false;
1767 break;
1768 default:
1769 first = false;
1770 myResolveErrors()
1771 .error(tr("Unexpected non scope object %1 (%2) reached in visitScopeChain")
1772 .arg(domTypeToString(current.internalKind()),
1773 current.canonicalPath().toString()))
1774 .handle(h);
1775 Q_ASSERT(false);
1776 break;
1777 }
1778 }
1779 return true;
1780}
1781
1783 const QString &symbolName, function_ref<bool(const DomItem &)> visitor, LookupOptions opts,
1784 const ErrorHandler &h, QSet<quintptr> *visited, QList<Path> *visitedRefs) const
1785{
1786 return visitScopeChain(
1787 [symbolName, visitor](const DomItem &obj) {
1788 return obj.visitLocalSymbolsNamed(symbolName,
1789 [visitor](const DomItem &el) { return visitor(el); });
1790 },
1791 opts, h, visited, visitedRefs);
1792}
1793
1795{
1797public:
1798 CppTypeInfo() = default;
1799
1800 static CppTypeInfo fromString(QStringView target, const ErrorHandler &h = nullptr)
1801 {
1802 CppTypeInfo res;
1803 QRegularExpression reTarget = QRegularExpression(QRegularExpression::anchoredPattern(
1804 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>\*?))"));
1805
1806 QRegularExpressionMatch m = reTarget.matchView(target);
1807 if (!m.hasMatch()) {
1809 .error(tr("Unexpected complex CppType %1").arg(target))
1810 .handle(h);
1811 }
1812 res.baseType = m.captured(u"baseType");
1813 res.isPointer = !m.captured(u"ptr").isEmpty();
1814 if (!m.captured(u"list").isEmpty()) {
1815 res.isList = true;
1816 res.baseType = m.captured(u"list");
1817 res.isPointer = !m.captured(u"listPtr").isEmpty();
1818 }
1819 if (!m.captured(u"mapValue").isEmpty()) {
1820 res.isMap = true;
1821 if (m.captured(u"mapKey") != u"QString") {
1823 .error(tr("Unexpected complex CppType %1 (map with non QString key)")
1824 .arg(target))
1825 .handle(h);
1826 }
1827 res.baseType = m.captured(u"mapValue");
1828 res.isPointer = !m.captured(u"mapPtr").isEmpty();
1829 }
1830 return res;
1831 }
1832
1834 bool isPointer = false;
1835 bool isMap = false;
1836 bool isList = false;
1837};
1838
1839static bool visitForLookupType(const DomItem &el, LookupType lookupType,
1840 function_ref<bool(const DomItem &)> visitor)
1841{
1842 bool correctType = false;
1843 DomType iType = el.internalKind();
1844 switch (lookupType) {
1845 case LookupType::Binding:
1846 correctType = (iType == DomType::Binding);
1847 break;
1848 case LookupType::Method:
1849 correctType = (iType == DomType::MethodInfo);
1850 break;
1851 case LookupType::Property:
1852 correctType = (iType == DomType::PropertyDefinition || iType == DomType::Binding);
1853 break;
1854 case LookupType::PropertyDef:
1855 correctType = (iType == DomType::PropertyDefinition);
1856 break;
1857 case LookupType::Type:
1858 correctType = (iType == DomType::Export); // accept direct QmlObject ref?
1859 break;
1860 default:
1861 Q_ASSERT(false);
1862 break;
1863 }
1864 if (correctType)
1865 return visitor(el);
1866 return true;
1867}
1868
1870 const DomItem &newIt, const QStringList &subpath,
1871 function_ref<bool(const DomItem &)> visitor, LookupType lookupType,
1872 const ErrorHandler &errorHandler, QList<Path> *visitedRefs)
1873{
1874 QVector<ResolveToDo> lookupToDos(
1875 { ResolveToDo{ newIt, 1 } }); // invariant: always increase pathIndex to guarantee
1876 // end even with only partial visited match
1877 QList<QSet<quintptr>> lookupVisited(subpath.size() + 1);
1878 while (!lookupToDos.isEmpty()) {
1879 ResolveToDo tNow = lookupToDos.takeFirst();
1880 auto vNow = std::make_pair(tNow.item.id(), tNow.pathIndex);
1881 DomItem subNow = tNow.item;
1882 int iSubPath = tNow.pathIndex;
1883 Q_ASSERT(iSubPath < subpath.size());
1884 QString subPathNow = subpath[iSubPath++];
1885 DomItem scope = subNow.proceedToScope();
1886 if (iSubPath < subpath.size()) {
1887 if (vNow.first != 0) {
1888 if (lookupVisited[vNow.second].contains(vNow.first))
1889 continue;
1890 else
1891 lookupVisited[vNow.second].insert(vNow.first);
1892 }
1893 if (scope.internalKind() == DomType::QmlObject)
1894 scope.visitDirectAccessibleScopes(
1895 [&lookupToDos, &subPathNow, iSubPath](const DomItem &el) {
1896 return el.visitLocalSymbolsNamed(
1897 subPathNow, [&lookupToDos, iSubPath](const DomItem &subEl) {
1898 lookupToDos.append({ subEl, iSubPath });
1899 return true;
1900 });
1901 },
1902 VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]),
1903 visitedRefs);
1904 } else {
1905 bool cont = scope.visitDirectAccessibleScopes(
1906 [&visitor, &subPathNow, lookupType](const DomItem &el) -> bool {
1907 if (lookupType == LookupType::Symbol)
1908 return el.visitLocalSymbolsNamed(subPathNow, visitor);
1909 else
1910 return el.visitLocalSymbolsNamed(
1911 subPathNow, [lookupType, &visitor](const DomItem &el) -> bool {
1912 return visitForLookupType(el, lookupType, visitor);
1913 });
1914 },
1915 VisitPrototypesOption::Normal, errorHandler, &(lookupVisited[vNow.second]),
1916 visitedRefs);
1917 if (!cont)
1918 return false;
1919 }
1920 }
1921 return true;
1922}
1923
1925 const QString &target, function_ref<bool(const DomItem &)> visitor, LookupType lookupType,
1926 LookupOptions opts, const ErrorHandler &errorHandler, QSet<quintptr> *visited,
1927 QList<Path> *visitedRefs) const
1928{
1929 if (target.isEmpty())
1930 return true;
1931 switch (lookupType) {
1932 case LookupType::Binding:
1933 case LookupType::Method:
1934 case LookupType::Property:
1935 case LookupType::PropertyDef:
1936 case LookupType::Symbol:
1937 case LookupType::Type: {
1938 QStringList subpath = target.split(QChar::fromLatin1('.'));
1939 if (subpath.size() == 1) {
1940 return visitLookup1(subpath.first(), visitor, opts, errorHandler, visited, visitedRefs);
1941 } else {
1942 return visitLookup1(
1943 subpath.at(0),
1944 [&subpath, visitor, lookupType, &errorHandler,
1945 visitedRefs](const DomItem &newIt) -> bool {
1946 return visitQualifiedNameLookup(newIt, subpath, visitor, lookupType,
1947 errorHandler, visitedRefs);
1948 },
1949 opts, errorHandler, visited, visitedRefs);
1950 }
1951 break;
1952 }
1953 case LookupType::CppType: {
1954 QString baseTarget = CppTypeInfo::fromString(target, errorHandler).baseType;
1955 DomItem localQmltypes = owner();
1956 while (localQmltypes && localQmltypes.internalKind() != DomType::QmltypesFile) {
1957 localQmltypes = localQmltypes.containingObject();
1958 localQmltypes = localQmltypes.owner();
1959 }
1960 if (localQmltypes) {
1961 if (DomItem localTypes = localQmltypes.field(Fields::components).key(baseTarget)) {
1962 bool cont = localTypes.visitIndexes([&visitor](const DomItem &els) {
1963 return els.visitIndexes([&visitor](const DomItem &el) {
1964 if (DomItem obj = el.field(Fields::objects).index(0))
1965 return visitor(obj);
1966 return true;
1967 });
1968 });
1969 if (!cont)
1970 return false;
1971 }
1972 }
1973 DomItem qmltypes = environment().field(Fields::qmltypesFileWithPath);
1974 return qmltypes.visitKeys([baseTarget, &visitor](const QString &, const DomItem &els) {
1975 DomItem comps =
1976 els.field(Fields::currentItem).field(Fields::components).key(baseTarget);
1977 return comps.visitIndexes([&visitor](const DomItem &el) {
1978 if (DomItem obj = el.field(Fields::objects).index(0))
1979 return visitor(obj);
1980 return true;
1981 });
1982 });
1983 break;
1984 }
1985 }
1986 Q_ASSERT(false);
1987 return true;
1988}
1989
1990/*!
1991 \internal
1992 \brief Dereference DomItems pointing to other DomItems.
1993
1994 Dereferences DomItems with internalKind being References, Export and Id.
1995 Also does multiple rounds of resolving for nested DomItems.
1996 Prefer this over \l {DomItem::get}.
1997 */
1998DomItem DomItem::proceedToScope(const ErrorHandler &h, QList<Path> *visitedRefs) const
1999{
2000 // follow references, resolve exports
2001 DomItem current = *this;
2002 while (current) {
2003 switch (current.internalKind()) {
2004 case DomType::Reference: {
2005 Path currentPath = current.canonicalPath();
2006 current = current.get(h, visitedRefs);
2007 break;
2008 }
2009 case DomType::Export:
2010 current = current.field(Fields::type);
2011 break;
2012 case DomType::Id:
2013 current = current.field(Fields::referredObject);
2014 break;
2015 default:
2016 return current.scope();
2017 break;
2018 }
2019 }
2020 return DomItem();
2021}
2022
2023QList<DomItem> DomItem::lookup(const QString &symbolName, LookupType type, LookupOptions opts,
2024 const ErrorHandler &errorHandler) const
2025{
2026 QList<DomItem> res;
2027 visitLookup(
2028 symbolName,
2029 [&res](const DomItem &el) {
2030 res.append(el);
2031 return true;
2032 },
2033 type, opts, errorHandler);
2034 return res;
2035}
2036
2037DomItem DomItem::lookupFirst(const QString &symbolName, LookupType type, LookupOptions opts,
2038 const ErrorHandler &errorHandler) const
2039{
2040 DomItem res;
2041 visitLookup(
2042 symbolName,
2043 [&res](const DomItem &el) {
2044 res = el;
2045 return false;
2046 },
2047 type, opts, errorHandler);
2048 return res;
2049}
2050
2052{
2053 return visitEl([](auto &&b) { return b->id(); });
2054}
2055
2057{
2058 return visitEl([](auto &&e) { return e->pathFromOwner(); });
2059}
2060
2062{
2063 return visitEl([this](auto &&e) { return e->canonicalFilePath(*this); });
2064}
2065
2067{
2068 if (m_kind == DomType::Empty)
2069 return MutableDomItem();
2070 DomItem o = owner();
2071 if (option == CopyOption::EnvDisconnected) {
2072 DomItem newItem = std::visit([this, &o](auto &&el) {
2073 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) {
2074 return DomItem();
2075 } else {
2076 auto copyPtr = el->makeCopy(o);
2077 return DomItem(m_top, copyPtr, m_ownerPath, copyPtr.get());
2078 }
2079 }, m_owner);
2081 }
2082 DomItem env = environment();
2083 std::shared_ptr<DomEnvironment> newEnvPtr;
2084 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
2085 newEnvPtr = std::make_shared<DomEnvironment>(envPtr, envPtr->loadPaths(), envPtr->options(),
2086 envPtr->domCreationOption());
2087 DomBase *eBase = envPtr.get();
2088 if (std::holds_alternative<const DomEnvironment *>(m_element) && eBase
2089 && std::get<const DomEnvironment *>(m_element) == eBase)
2090 return MutableDomItem(DomItem(newEnvPtr));
2091 } else if (std::shared_ptr<DomUniverse> univPtr = top().ownerAs<DomUniverse>()) {
2092 newEnvPtr = std::make_shared<DomEnvironment>(
2093 QStringList(),
2094 DomEnvironment::Option::SingleThreaded | DomEnvironment::Option::NoDependencies,
2096 } else {
2097 Q_ASSERT(false);
2098 return {};
2099 }
2100 DomItem newItem = std::visit(
2101 [this, newEnvPtr, &o](auto &&el) {
2102 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>) {
2103 return DomItem();
2104 } else {
2105 auto copyPtr = el->makeCopy(o);
2106 return DomItem(newEnvPtr, copyPtr, m_ownerPath, copyPtr.get());
2107 }
2108 },
2109 m_owner);
2110
2111 switch (o.internalKind()) {
2113 newEnvPtr->addQmlDirectory(newItem.ownerAs<QmlDirectory>(), AddOption::Overwrite);
2114 break;
2115 case DomType::JsFile:
2116 newEnvPtr->addJsFile(newItem.ownerAs<JsFile>(), AddOption::Overwrite);
2117 break;
2118 case DomType::QmlFile:
2119 newEnvPtr->addQmlFile(newItem.ownerAs<QmlFile>(), AddOption::Overwrite);
2120 break;
2122 newEnvPtr->addQmltypesFile(newItem.ownerAs<QmltypesFile>(), AddOption::Overwrite);
2123 break;
2124 case DomType::GlobalScope: {
2125 newEnvPtr->addGlobalScope(newItem.ownerAs<GlobalScope>(), AddOption::Overwrite);
2126 } break;
2127 case DomType::ModuleIndex:
2128 case DomType::MockOwner:
2129 case DomType::ScriptExpression:
2130 case DomType::AstComments:
2131 case DomType::LoadInfo:
2132 case DomType::FileLocationsNode:
2133 case DomType::DomEnvironment:
2134 case DomType::DomUniverse:
2135 qCWarning(domLog) << "DomItem::makeCopy " << internalKindStr()
2136 << " does not support binding to environment";
2137 Q_ASSERT(false);
2138 return MutableDomItem();
2139 default:
2140 qCWarning(domLog) << "DomItem::makeCopy(" << internalKindStr()
2141 << ") is not an known OwningItem";
2142 Q_ASSERT(o.isOwningItem());
2143 return MutableDomItem();
2144 }
2145 DomItem newEnv(newEnvPtr);
2146 Q_ASSERT(newEnv.path(o.canonicalPath()).m_owner == newItem.m_owner);
2148}
2149
2150bool DomItem::commitToBase(const std::shared_ptr<DomEnvironment> &validEnvPtr) const
2151{
2152 DomItem env = environment();
2153 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
2154 return envPtr->commitToBase(env, validEnvPtr);
2155 }
2156 return false;
2157}
2158
2159bool DomItem::visitLocalSymbolsNamed(const QString &name, function_ref<bool(const DomItem &)> visitor) const
2160{
2161 if (name.isEmpty()) // no empty symbol
2162 return true;
2163 // we could avoid discriminating by type and just access all the needed stuff in the correct
2164 // sequence, making sure it is empty if not provided, but for now I find it clearer to check the
2165 // type
2166 DomItem f;
2167 DomItem v;
2168 switch (internalKind()) {
2169 case DomType::QmlObject:
2170 f = field(Fields::propertyDefs);
2171 v = f.key(name);
2172 if (!v.visitIndexes(visitor))
2173 return false;
2174 f = field(Fields::bindings);
2175 v = f.key(name);
2176 if (!v.visitIndexes(visitor))
2177 return false;
2178 f = field(Fields::methods);
2179 v = f.key(name);
2180 if (!v.visitIndexes(visitor))
2181 return false;
2182 break;
2184 // to do
2185 break;
2186 case DomType::QmlComponent:
2187 f = field(Fields::ids);
2188 v = f.key(name);
2189 if (!v.visitIndexes(visitor))
2190 return false;
2191 Q_FALLTHROUGH();
2192 case DomType::JsResource:
2193 case DomType::GlobalComponent:
2194 case DomType::QmltypesComponent:
2195 f = field(Fields::enumerations);
2196 v = f.key(name);
2197 if (!v.visitIndexes(visitor))
2198 return false;
2199 break;
2200 case DomType::MethodInfo: {
2201 DomItem params = field(Fields::parameters);
2202 if (!params.visitIndexes([name, visitor](const DomItem &p) {
2203 const MethodParameter *pPtr = p.as<MethodParameter>();
2204 if (pPtr->name == name && !visitor(p))
2205 return false;
2206 return true;
2207 }))
2208 return false;
2209 break;
2210 }
2211 case DomType::QmlFile: {
2212 f = field(Fields::components);
2213 v = f.key(name);
2214 if (!v.visitIndexes(visitor))
2215 return false;
2216 break;
2217 }
2218 case DomType::ImportScope: {
2219 f = field(Fields::imported);
2220 v = f.key(name);
2221 if (!v.visitIndexes(visitor))
2222 return false;
2223 f = field(Fields::qualifiedImports);
2224 v = f.key(name);
2225 if (v && !visitor(v))
2226 return false;
2227 break;
2228 default:
2229 Q_ASSERT(!isScope());
2230 break;
2231 }
2232 }
2233 return true;
2234}
2235
2236DomItem DomItem::operator[](const QString &cName) const
2237{
2238 if (internalKind() == DomType::Map)
2239 return key(cName);
2240 return field(cName);
2241}
2242
2243DomItem DomItem::operator[](QStringView cName) const
2244{
2245 if (internalKind() == DomType::Map)
2246 return key(cName.toString());
2247 return field(cName);
2248}
2249
2250DomItem DomItem::operator[](const Path &p) const
2251{
2252 return path(p);
2253}
2254
2256{
2257 return base()->value();
2258}
2259
2260void DomItem::dumpPtr(const Sink &sink) const
2261{
2262 sink(u"DomItem{ topPtr:");
2263 sink(QString::number((quintptr)topPtr().get(), 16));
2264 sink(u", ownerPtr:");
2265 sink(QString::number((quintptr)owningItemPtr().get(), 16));
2266 sink(u", ownerPath:");
2267 m_ownerPath.dump(sink);
2268 sink(u", elPtr:");
2269 sink(QString::number((quintptr)base(),16));
2270 sink(u"}");
2271}
2272
2274 const Sink &s, int indent,
2275 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2276{
2277 visitEl([this, s, indent, filter](auto &&e) { e->dump(*this, s, indent, filter); });
2278}
2279
2281DomItem::dump(const QString &path,
2282 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter,
2283 int nBackups, int indent, FileWriter *fw) const
2284{
2285 FileWriter localFw;
2286 if (!fw)
2287 fw = &localFw;
2288 switch (fw->write(
2289 path,
2290 [this, indent, filter](QTextStream &ts) {
2291 this->dump([&ts](QStringView s) { ts << s; }, indent, filter);
2292 return true;
2293 },
2294 nBackups)) {
2295 case FileWriter::Status::ShouldWrite:
2296 case FileWriter::Status::SkippedDueToFailure:
2297 qWarning() << "Failure dumping " << canonicalPath() << " to " << path;
2298 break;
2299 case FileWriter::Status::DidWrite:
2300 case FileWriter::Status::SkippedEqual:
2301 break;
2302 }
2303 return fw->status;
2304}
2305
2307{
2308 return dumperToString([this](const Sink &s){ dump(s); });
2309}
2310
2312{
2313 return std::visit([](auto &&ow) {
2314 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2315 return 0;
2316 else
2317 return ow->derivedFrom();
2318 }, m_owner);
2319}
2320
2321int DomItem::revision() const
2322{
2323 return std::visit([](auto &&ow) {
2324 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2325 return -1;
2326 else
2327 return ow->revision();
2328 }, m_owner);
2329}
2330
2332{
2333 return std::visit([](auto &&ow) {
2334 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2335 return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
2336 else
2337 return ow->createdAt();
2338 }, m_owner);
2339}
2340
2342{
2343 return std::visit([](auto &&ow) {
2344 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2345 return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
2346 else
2347 return ow->frozenAt();
2348 }, m_owner);
2349}
2350
2352{
2353 return std::visit([](auto &&ow) {
2354 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2355 return QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC);
2356 else
2357 return ow->lastDataUpdateAt();
2358 }, m_owner);
2359}
2360
2362{
2363 std::visit([this, &msg](auto &&ow) {
2364 if constexpr (std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2365 defaultErrorHandler(msg.withItem(*this));
2366 else
2367 ow->addError(owner(), std::move(msg.withItem(*this)));
2368 }, m_owner);
2369}
2370
2372{
2373 // We need a copy here. Error handlers may be called when this is gone.
2374 return [self = *this](const ErrorMessage &m) { self.addError(ErrorMessage(m)); };
2375}
2376
2377void DomItem::clearErrors(const ErrorGroups &groups, bool iterate) const
2378{
2379 std::visit([&groups](auto &&ow) {
2380 if constexpr (!std::is_same_v<std::decay_t<decltype(ow)>, std::monostate>)
2381 ow->clearErrors(groups);
2382 }, m_owner);
2383
2384 if (iterate) {
2385 iterateSubOwners([groups](const DomItem &i){
2386 i.clearErrors(groups, true);
2387 return true;
2388 });
2389 }
2390}
2391
2393 function_ref<bool(const DomItem &, const ErrorMessage &)> visitor, bool iterate,
2394 Path inPath) const
2395{
2396 if (!std::visit([this, visitor, inPath](auto &&el) {
2397 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2398 return true;
2399 else
2400 return el->iterateErrors(owner(), visitor, inPath);
2401 }, m_owner)) {
2402 return false;
2403 }
2404
2405 if (iterate && !iterateSubOwners([inPath, visitor](const DomItem &i) {
2406 return i.iterateErrors(visitor, true, inPath);
2407 })) {
2408 return false;
2409 }
2410
2411 return true;
2412}
2413
2414bool DomItem::iterateSubOwners(function_ref<bool(const DomItem &)> visitor) const
2415{
2416 return std::visit([this, visitor](auto &&o) {
2417 if constexpr (std::is_same_v<std::decay_t<decltype(o)>, std::monostate>)
2418 return true;
2419 else
2420 return o->iterateSubOwners(owner(), visitor);
2421 }, m_owner);
2422}
2423
2424bool DomItem::iterateDirectSubpaths(DirectVisitor v) const
2425{
2426 return visitEl(
2427 [this, v](auto &&el) { return el->iterateDirectSubpaths(*this, v); });
2428}
2429
2430DomItem DomItem::subReferencesItem(const PathEls::PathComponent &c, const QList<Path> &paths) const
2431{
2432 return subListItem(
2433 List::fromQList<Path>(pathFromOwner().withComponent(c), paths,
2434 [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
2435 return list.subReferenceItem(p, el);
2436 }));
2437}
2438
2439DomItem DomItem::subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const
2440{
2442 return DomItem(m_top, m_owner, m_ownerPath, Reference(referencedObject, Path(c)));
2443 } else {
2444 return DomItem(m_top, m_owner, m_ownerPath,
2445 Reference(referencedObject, pathFromOwner().withComponent(c)));
2446 }
2447}
2448
2449shared_ptr<DomTop> DomItem::topPtr() const
2450{
2451 return std::visit([](auto &&el) -> shared_ptr<DomTop> {
2452 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2453 return {};
2454 else
2455 return el;
2456 }, m_top);
2457}
2458
2460{
2461 return std::visit([](auto &&el) -> shared_ptr<OwningItem> {
2462 if constexpr (std::is_same_v<std::decay_t<decltype(el)>, std::monostate>)
2463 return {};
2464 else
2465 return el;
2466 }, m_owner);
2467}
2468
2469/*!
2470 \internal
2471 Returns a pointer to the virtual base pointer to a DomBase.
2472*/
2473const DomBase *DomItem::base() const
2474{
2475 return visitEl([](auto &&el) -> const DomBase * { return el->domBase(); });
2476}
2477
2478DomItem::DomItem(const std::shared_ptr<DomEnvironment> &envPtr):
2479 DomItem(envPtr, envPtr, Path(), envPtr.get())
2480{
2481}
2482
2483DomItem::DomItem(const std::shared_ptr<DomUniverse> &universePtr):
2484 DomItem(universePtr, universePtr, Path(), universePtr.get())
2485{
2486}
2487
2488/*!
2489\brief Creates a new document with the given code
2490
2491This is mostly useful for testing or loading a single code snippet without any dependency.
2492The fileType should normally be QmlFile, but you might want to load a qmltypes file for
2493example and interpret it as qmltypes file (not plain Qml), or as JsFile. In those case
2494set the file type accordingly.
2495*/
2496DomItem DomItem::fromCode(const QString &code, DomType fileType)
2497{
2498 if (code.isEmpty())
2499 return DomItem();
2500
2501 auto env = DomEnvironment::create({}, DomEnvironment::Option::NoDependencies);
2502 DomItem tFile;
2503 env->loadFile(
2504 FileToLoad::fromMemory(env, QString(), code),
2505 [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
2506 std::make_optional(fileType));
2507 return tFile.fileObject();
2508}
2509
2510Empty::Empty()
2511{}
2512
2513Path Empty::pathFromOwner() const
2514{
2515 return Path();
2516}
2517
2518Path Empty::canonicalPath(const DomItem &) const
2519{
2520 return Path();
2521}
2522
2524{
2525 return true;
2526}
2527
2528DomItem Empty::containingObject(const DomItem &self) const
2529{
2530 return self;
2531}
2532
2533void Empty::dump(
2534 const DomItem &, const Sink &s, int,
2535 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)>) const
2536{
2537 s(u"null");
2538}
2539
2540Map::Map(const Path &pathFromOwner, const Map::LookupFunction &lookup,
2541 const Keys &keys, const QString &targetType)
2543{}
2544
2546{
2547 return quintptr(0);
2548}
2549
2550bool Map::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2551{
2552 QSet<QString> ksSet = keys(self);
2553 QStringList ksList = QStringList(ksSet.begin(), ksSet.end());
2554 std::sort(ksList.begin(), ksList.end());
2555 for (const QString &k : std::as_const(ksList)) {
2556 if (!visitor(PathEls::Key(k), [&self, this, k]() { return key(self, k); }))
2557 return false;
2558 }
2559 return true;
2560}
2561
2562const QSet<QString> Map::keys(const DomItem &self) const
2563{
2564 return m_keys(self);
2565}
2566
2567DomItem Map::key(const DomItem &self, const QString &name) const
2568{
2569 return m_lookup(self, name);
2570}
2571
2573 const DomItem &self, const Sink &sink, int indent,
2574 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2575{
2576 bool comma = false;
2577 DomKind dK = self.domKind();
2578 switch (dK) {
2579 case DomKind::Object:
2580 sink(u"{ \"~type~\":");
2581 sinkEscaped(sink, typeName());
2582 comma = true;
2583 break;
2584 case DomKind::Value:
2585 {
2586 sink(QString::fromUtf8(value().toJsonValue().toJson()));
2587 break;
2588 }
2589 case DomKind::Empty:
2590 sink(u"null");
2591 break;
2592 case DomKind::List:
2593 sink(u"[");
2594 break;
2595 case DomKind::Map:
2596 sink(u"{");
2597 break;
2598 case DomKind::ScriptElement:
2599 // nothing to print
2600 break;
2601 }
2602 auto closeParens = qScopeGuard(
2603 [dK, sink, indent]{
2604 switch (dK) {
2605 case DomKind::Object:
2606 sinkNewline(sink, indent);
2607 sink(u"}");
2608 break;
2609 case DomKind::Value:
2610 break;
2611 case DomKind::Empty:
2612 break;
2613 case DomKind::List:
2614 sinkNewline(sink, indent);
2615 sink(u"]");
2616 break;
2617 case DomKind::Map:
2618 sinkNewline(sink, indent);
2619 sink(u"}");
2620 break;
2621 case DomKind::ScriptElement:
2622 // nothing to print
2623 break;
2624 }
2625 });
2626 index_type idx = 0;
2627 self.iterateDirectSubpaths(
2628 [&comma, &idx, dK, sink, indent, &self, filter](const PathEls::PathComponent &c,
2629 function_ref<DomItem()> itemF) {
2630 DomItem i = itemF();
2631 if (!filter(self, c, i))
2632 return true;
2633 if (comma)
2634 sink(u",");
2635 else
2636 comma = true;
2637 switch (c.kind()) {
2638 case Path::Kind::Field:
2639 sinkNewline(sink, indent + 2);
2640 if (dK != DomKind::Object)
2641 sink(u"UNEXPECTED ENTRY ERROR:");
2642 sinkEscaped(sink, c.name());
2643 sink(u":");
2644 break;
2645 case Path::Kind::Key:
2646 sinkNewline(sink, indent + 2);
2647 if (dK != DomKind::Map)
2648 sink(u"UNEXPECTED ENTRY ERROR:");
2649 sinkEscaped(sink, c.name());
2650 sink(u":");
2651 break;
2652 case Path::Kind::Index:
2653 sinkNewline(sink, indent + 2);
2654 if (dK != DomKind::List)
2655 sink(u"UNEXPECTED ENTRY ERROR:");
2656 else if (idx++ != c.index())
2657 sink(u"OUT OF ORDER ARRAY:");
2658 break;
2659 default:
2660 sinkNewline(sink, indent + 2);
2661 sink(u"UNEXPECTED PATH KIND ERROR (ignored)");
2662 break;
2663 }
2664 if (self.isCanonicalChild(i)) {
2665 i.dump(sink, indent + 2, filter);
2666 } else {
2667 sink(uR"({ "~type~": "Reference", "immediate": true, "referredObjectPath":")");
2668 i.canonicalPath().dump([sink](QStringView s) {
2669 sinkEscaped(sink, s, EscapeOptions::NoOuterQuotes);
2670 });
2671 sink(u"\"}");
2672 }
2673 return true;
2674 });
2675}
2676
2677List::List(const Path &pathFromOwner, const List::LookupFunction &lookup,
2678 const List::Length &length, const List::IteratorFunction &iterator,
2679 const QString &elType):
2682{}
2683
2685{
2686 return quintptr(0);
2687}
2688
2690 const DomItem &self, const Sink &sink, int indent,
2691 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter) const
2692{
2693 bool first = true;
2694 sink(u"[");
2695 iterateDirectSubpaths(
2696 self,
2697 [&self, indent, &first, sink, filter](const PathEls::PathComponent &c,
2698 function_ref<DomItem()> itemF) {
2699 DomItem item = itemF();
2700 if (!filter(self, c, item))
2701 return true;
2702 if (first)
2703 first = false;
2704 else
2705 sink(u",");
2706 sinkNewline(sink, indent + 2);
2707 item.dump(sink, indent + 2, filter);
2708 return true;
2709 });
2710 sink(u"]");
2711}
2712
2713bool List::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2714{
2715 if (m_iterator) {
2716 return m_iterator(self, [visitor](index_type i, function_ref<DomItem()> itemF) {
2717 return visitor(PathEls::Index(i), itemF);
2718 });
2719 }
2720 index_type len = indexes(self);
2721 for (index_type i = 0; i < len; ++i) {
2722 if (!visitor(PathEls::Index(i), [this, &self, i]() { return index(self, i); }))
2723 return false;
2724 }
2725 return true;
2726}
2727
2728index_type List::indexes(const DomItem &self) const
2729{
2730 return m_length(self);
2731}
2732
2733DomItem List::index(const DomItem &self, index_type index) const
2734{
2735 return m_lookup(self, index);
2736}
2737
2738void List::writeOut(const DomItem &self, OutWriter &ow, bool compact) const
2739{
2740 const auto fLoc = FileLocations::treeOf(self);
2741 ow.writeRegion(fLoc, LeftBracketRegion);
2742 bool first = true;
2743 iterateDirectSubpaths(self,
2744 [&ow, &first, &fLoc, compact](const PathEls::PathComponent &,
2745 function_ref<DomItem()> elF) {
2746 if (first)
2747 first = false;
2748 else
2749 ow.writeRegion(fLoc, CommaTokenRegion).ensureSpace();
2750 if (!compact)
2751 ow.ensureNewline(1);
2752 DomItem el = elF();
2753 el.writeOut(ow);
2754 return true;
2755 });
2756 if (!compact && !first)
2757 ow.newline();
2758 ow.writeRegion(fLoc, RightBracketRegion);
2759}
2760
2762
2764{
2765 Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
2766 return self.owner().canonicalPath().withPath(m_pathFromOwner);
2767}
2768
2770{
2771 Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
2772 return DomBase::containingObject(self);
2773}
2774
2776{
2777 //if (!domTypeCanBeInline(kind()))
2778 m_pathFromOwner = newPath;
2779}
2780
2782{
2783 for (const Path &p : referredObjectPath) {
2784 switch (p.headKind()) {
2785 case Path::Kind::Current:
2786 switch (p.headCurrent()) {
2787 case PathCurrent::Lookup:
2788 case PathCurrent::LookupDynamic:
2789 case PathCurrent::LookupStrict:
2790 case PathCurrent::ObjChain:
2791 case PathCurrent::ScopeChain:
2792 return true;
2793 default:
2794 break;
2795 }
2796 break;
2797 case Path::Kind::Empty:
2798 case Path::Kind::Any:
2799 case Path::Kind::Filter:
2800 return true;
2801 default:
2802 break;
2803 }
2804 }
2805 return false;
2806}
2807
2808Reference::Reference(const Path &referredObject, const Path &pathFromOwner, const SourceLocation &)
2810{
2811}
2812
2814{
2815 return quintptr(0);
2816}
2817
2818bool Reference::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2819{
2820 bool cont = true;
2821 cont = cont && self.dvValueLazyField(visitor, Fields::referredObjectPath, [this]() {
2822 return referredObjectPath.toString();
2823 });
2824 cont = cont
2825 && self.dvItemField(visitor, Fields::get, [this, &self]() { return this->get(self); });
2826 return cont;
2827}
2828
2829DomItem Reference::field(const DomItem &self, QStringView name) const
2830{
2831 if (Fields::referredObjectPath == name)
2832 return self.subDataItemField(Fields::referredObjectPath, referredObjectPath.toString());
2833 if (Fields::get == name)
2834 return get(self);
2835 return DomItem();
2836}
2837
2839{
2840 return QList<QString>({Fields::referredObjectPath.toString(), Fields::get.toString()});
2841}
2842
2844{
2845 return DomItem();
2846}
2847
2848DomItem Reference::key(const DomItem &, const QString &) const
2849{
2850 return DomItem();
2851}
2852
2853DomItem Reference::get(const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const
2854{
2855 DomItem res;
2856 if (referredObjectPath) {
2857 DomItem env;
2858 Path selfPath;
2859 Path cachedPath;
2860 if (shouldCache()) {
2861 env = self.environment();
2862 if (env) {
2863 selfPath = self.canonicalPath();
2864 RefCacheEntry cached = RefCacheEntry::forPath(self, selfPath);
2865 switch (cached.cached) {
2866 case RefCacheEntry::Cached::None:
2867 break;
2868 case RefCacheEntry::Cached::First:
2869 case RefCacheEntry::Cached::All:
2870 if (!cached.canonicalPaths.isEmpty())
2871 cachedPath = cached.canonicalPaths.first();
2872 else
2873 return res;
2874 break;
2875 }
2876 if (cachedPath) {
2877 res = env.path(cachedPath);
2878 if (!res)
2879 qCWarning(refLog) << "referenceCache outdated, reference at " << selfPath
2880 << " leads to invalid path " << cachedPath;
2881 else
2882 return res;
2883 }
2884 }
2885 }
2886 QList<Path> visitedRefsLocal;
2887 self.resolve(
2888 referredObjectPath,
2889 [&res](Path, const DomItem &el) {
2890 res = el;
2891 return false;
2892 },
2893 h, ResolveOption::None, referredObjectPath,
2894 (visitedRefs ? visitedRefs : &visitedRefsLocal));
2895 if (env)
2896 RefCacheEntry::addForPath(
2897 env, selfPath, RefCacheEntry { RefCacheEntry::Cached::First, { cachedPath } });
2898 }
2899 return res;
2900}
2901
2903 const DomItem &self, const ErrorHandler &h, QList<Path> *visitedRefs) const
2904{
2905 QList<DomItem> res;
2906 if (referredObjectPath) {
2907 DomItem env;
2908 Path selfPath;
2909 QList<Path> cachedPaths;
2910 if (shouldCache()) {
2911 selfPath = canonicalPath(self);
2912 env = self.environment();
2913 RefCacheEntry cached = RefCacheEntry::forPath(env, selfPath);
2914 switch (cached.cached) {
2915 case RefCacheEntry::Cached::None:
2916 case RefCacheEntry::Cached::First:
2917 break;
2918 case RefCacheEntry::Cached::All:
2919 cachedPaths += cached.canonicalPaths;
2920 if (cachedPaths.isEmpty())
2921 return res;
2922 }
2923 }
2924 if (!cachedPaths.isEmpty()) {
2925 bool outdated = false;
2926 for (const Path &p : cachedPaths) {
2927 DomItem newEl = env.path(p);
2928 if (!newEl) {
2929 outdated = true;
2930 qCWarning(refLog) << "referenceCache outdated, reference at " << selfPath
2931 << " leads to invalid path " << p;
2932 break;
2933 } else {
2934 res.append(newEl);
2935 }
2936 }
2937 if (outdated) {
2938 res.clear();
2939 } else {
2940 return res;
2941 }
2942 }
2943 self.resolve(
2944 referredObjectPath,
2945 [&res](Path, const DomItem &el) {
2946 res.append(el);
2947 return true;
2948 },
2949 h, ResolveOption::None, referredObjectPath, visitedRefs);
2950 if (env) {
2951 QList<Path> canonicalPaths;
2952 for (const DomItem &i : res) {
2953 if (i)
2954 canonicalPaths.append(i.canonicalPath());
2955 else
2956 qCWarning(refLog)
2957 << "getAll of reference at " << selfPath << " visits empty items.";
2958 }
2959 RefCacheEntry::addForPath(
2960 env, selfPath,
2961 RefCacheEntry { RefCacheEntry::Cached::All, std::move(canonicalPaths) });
2962 }
2963 }
2964 return res;
2965}
2966
2967/*!
2968\internal
2969\class QQmlJS::Dom::OwningItem
2970
2971\brief A DomItem that owns other DomItems and is managed through a shared pointer
2972
2973This is the unit of update of the Dom model, only OwningItem can be updated after
2974exposed, to update a single element one has to copy its owner, change it, and expose an new one.
2975The non owning contents of an exposed OwiningItem can safely be considered immutable, and pointers
2976to them must remain valid for the whole lifetime of the OwningItem (that is managed with
2977shared_ptr), only the sub OwningItems *might* be change.
2978The OwningItem has a mutex that controls it access (mainly for the errors, observers, and sub
2979OwningItems), Access to the rest is *not* controlled, it should either be by a single thread
2980(during construction), or immutable (after pubblication).
2981
2982*/
2983
2984OwningItem::OwningItem(int derivedFrom)
2985 : m_derivedFrom(derivedFrom),
2986 m_revision(nextRevision()),
2990{}
2991
2992OwningItem::OwningItem(int derivedFrom, const QDateTime &lastDataUpdateAt)
2993 : m_derivedFrom(derivedFrom),
2994 m_revision(nextRevision()),
2998{}
2999
3001 : m_derivedFrom(o.revision()),
3002 m_revision(nextRevision()),
3006{
3007 QMultiMap<Path, ErrorMessage> my_errors;
3008 {
3009 QMutexLocker l1(o.mutex());
3010 my_errors = o.m_errors;
3011
3012 }
3013 {
3014 QMutexLocker l2(mutex());
3015 m_errors = my_errors;
3016 }
3017}
3018
3019
3021{
3022 static QAtomicInt nextRev(0);
3023 return ++nextRev;
3024}
3025
3026bool OwningItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
3027{
3028 bool cont = true;
3029 cont = cont && self.dvItemField(visitor, Fields::errors, [&self, this]() {
3030 QMultiMap<Path, ErrorMessage> myErrors = localErrors();
3031 return self.subMapItem(Map(
3032 self.pathFromOwner().withField(Fields::errors),
3033 [myErrors](const DomItem &map, const QString &key) {
3034 auto it = myErrors.find(Path::fromString(key));
3035 if (it != myErrors.end())
3036 return map.subDataItem(PathEls::Key(key), it->toCbor(),
3037 ConstantData::Options::FirstMapIsFields);
3038 else
3039 return DomItem();
3040 },
3041 [myErrors](const DomItem &) {
3042 QSet<QString> res;
3043 auto it = myErrors.keyBegin();
3044 auto end = myErrors.keyEnd();
3045 while (it != end)
3046 res.insert(it++->toString());
3047 return res;
3048 },
3049 QLatin1String("ErrorMessages")));
3050 });
3051 return cont;
3052}
3053
3055{
3057 if (s.pathFromSource) {
3058 if (!s.pathToSource)
3059 return DomItem();
3060 return self.path(s.pathToSource);
3061 }
3062 return DomItem();
3063}
3064
3066{
3067 return m_derivedFrom;
3068}
3069
3071{
3072 return m_revision;
3073}
3074
3075bool OwningItem::frozen() const
3076{
3077 return m_frozenAt > m_createdAt;
3078}
3079
3081{
3082 if (!frozen()) {
3083 m_frozenAt = QDateTime::currentDateTimeUtc();
3084 if (m_frozenAt <= m_createdAt)
3085 m_frozenAt = m_createdAt.addSecs(1);
3086 return true;
3087 }
3088 return false;
3089}
3090
3092{
3093 return m_createdAt;
3094}
3095
3097{
3098 return m_lastDataUpdateAt;
3099}
3100
3102{
3103 return m_frozenAt;
3104}
3105
3106void OwningItem::refreshedDataAt(QDateTime tNew)
3107{
3108 if (m_lastDataUpdateAt < tNew) // remove check?
3109 m_lastDataUpdateAt = tNew;
3110}
3111
3113{
3114 addErrorLocal(std::move(msg));
3115}
3116
3118{
3119 QMutexLocker l(mutex());
3120 quint32 &c = m_errorsCounts[msg];
3121 c += 1;
3122 if (c == 1)
3123 m_errors.insert(msg.path, msg);
3124}
3125
3127{
3128 QMutexLocker l(mutex());
3129 auto it = m_errors.begin();
3130 while (it != m_errors.end()) {
3131 if (it->errorGroups == groups)
3132 it = m_errors.erase(it);
3133 else
3134 ++it;
3135 }
3136}
3137
3139 const DomItem &self, function_ref<bool(const DomItem &, const ErrorMessage &)> visitor,
3140 const Path &inPath)
3141{
3142 QMultiMap<Path, ErrorMessage> myErrors;
3143 {
3144 QMutexLocker l(mutex());
3145 myErrors = m_errors;
3146 }
3147 auto it = myErrors.lowerBound(inPath);
3148 auto end = myErrors.end();
3149 while (it != end && it.key().mid(0, inPath.length()) == inPath) {
3150 if (!visitor(self, *it++))
3151 return false;
3152 }
3153 return true;
3154}
3155
3156bool OwningItem::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor)
3157{
3158 return self.iterateDirectSubpaths(
3159 [&self, visitor](const PathEls::PathComponent &, function_ref<DomItem()> iF) {
3160 DomItem i = iF();
3161 if (i.owningItemPtr() != self.owningItemPtr()) {
3162 DomItem container = i.container();
3163 if (container.id() == self.id())
3164 return visitor(i);
3165 }
3166 return true;
3167 });
3168}
3169
3170bool operator==(const DomItem &o1, const DomItem &o2)
3171{
3172 if (o1.m_kind != o2.m_kind)
3173 return false;
3174 return o1.visitEl([&o1, &o2](auto &&el1) {
3175 auto &&el2 = std::get<std::decay_t<decltype(el1)>>(o2.m_element);
3176 auto id1 = el1->id();
3177 auto id2 = el2->id();
3178 if (id1 != id2)
3179 return false;
3180 if (id1 != quintptr(0))
3181 return true;
3182 if (o1.m_owner != o2.m_owner)
3183 return false;
3184 Path p1 = el1->pathFromOwner();
3185 Path p2 = el2->pathFromOwner();
3186 if (p1 != p2)
3187 return false;
3188 return true;
3189 });
3190}
3191
3193{
3194 MutableDomItem self;
3195 return [&self](const ErrorMessage &m) { self.addError(ErrorMessage(m)); };
3196}
3197
3199 const PropertyDefinition &propertyDef, AddOption option)
3200{
3201 if (QmlObject *el = mutableAs<QmlObject>())
3202 return el->addPropertyDef(*this, propertyDef, option);
3203 else
3204 Q_ASSERT(false && "addPropertyDef on non qml scope");
3205 return MutableDomItem();
3206}
3207
3209{
3210 if (QmlObject *el = mutableAs<QmlObject>())
3211 return el->addBinding(*this, binding, option);
3212 else
3213 Q_ASSERT(false && "addBinding on non qml scope");
3214 return MutableDomItem();
3215}
3216
3218{
3219 if (QmlObject *el = mutableAs<QmlObject>())
3220 return el->addMethod(*this, functionDef, option);
3221 else
3222 Q_ASSERT(false && "addMethod on non qml scope");
3223 return MutableDomItem();
3224}
3225
3227{
3228 if (QmlObject *el = mutableAs<QmlObject>()) {
3229 return el->addChild(*this, child);
3230 } else if (QmlComponent *el = mutableAs<QmlComponent>()) {
3231 Path p = el->addObject(child);
3232 return owner().path(p); // convenience: treat component objects as children
3233 } else {
3234 Q_ASSERT(false && "addChild on non qml scope");
3235 }
3236 return MutableDomItem();
3237}
3238
3239
3240QDebug operator<<(QDebug debug, const DomItem &c)
3241{
3242 dumperToQDebug([&c](const Sink &s) { c.dump(s); }, debug);
3243 return debug;
3244}
3245
3246QDebug operator<<(QDebug debug, const MutableDomItem &c)
3247{
3248 MutableDomItem cc(c);
3249 return debug.noquote().nospace() << "MutableDomItem(" << domTypeToString(cc.internalKind())
3250 << ", " << cc.canonicalPath().toString() << ")";
3251}
3252
3253bool ListPBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor v) const
3254{
3255 index_type len = index_type(m_pList.size());
3256 for (index_type i = 0; i < len; ++i) {
3257 if (!v(PathEls::Index(i), [this, &self, i] { return this->index(self, i); }))
3258 return false;
3259 }
3260 return true;
3261}
3262
3263void ListPBase::writeOut(const DomItem &self, OutWriter &ow, bool compact) const
3264{
3265 const auto fLoc = FileLocations::treeOf(self);
3266 ow.writeRegion(fLoc, LeftBracketRegion);
3267 bool first = true;
3268 index_type len = index_type(m_pList.size());
3269 for (index_type i = 0; i < len; ++i) {
3270 if (first)
3271 first = false;
3272 else
3273 ow.writeRegion(fLoc, CommaTokenRegion).ensureSpace();
3274 if (!compact)
3275 ow.ensureNewline(1);
3276 DomItem el = index(self, i);
3277 el.writeOut(ow);
3278 }
3279 if (!compact && !first)
3280 ow.newline();
3281 ow.writeRegion(fLoc, RightBracketRegion);
3282}
3283
3285{
3286 return m_scope;
3287}
3288void ScriptElement::setSemanticScope(const QQmlJSScope::ConstPtr &scope)
3289{
3290 m_scope = scope;
3291}
3292
3293/*!
3294 \internal
3295 \brief Returns a pointer to the virtual base for virtual method calls.
3296
3297 A helper to call virtual methods without having to call std::visit(...).
3298 */
3300{
3301 if (!m_data)
3302 return nullptr;
3303
3304 return std::visit(
3305 [](auto &&e) {
3306 // std::reinterpret_pointer_cast does not exist on qnx it seems...
3307 return std::shared_ptr<ScriptElement>(
3308 e, static_cast<ScriptElement *>(e.get()));
3309 },
3310 *m_data);
3311}
3312
3313std::shared_ptr<ExternalOwningItem> getFileItemOwner(const DomItem &fileItem)
3314{
3315 switch (fileItem.internalKind()) {
3316 case DomType::JsFile:
3317 return fileItem.ownerAs<JsFile>();
3318 case DomType::QmlFile:
3319 return fileItem.ownerAs<QmlFile>();
3320 default:
3321 Q_UNREACHABLE_RETURN({});
3322 }
3323}
3324
3325} // end namespace Dom
3326} // end namespace QQmlJS
3327
3328QT_END_NAMESPACE
3329
3330#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 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)
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
List(const Path &pathFromOwner, const LookupFunction &lookup, const Length &length, const IteratorFunction &iterator, const QString &elType)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
std::function< bool(const DomItem &, function_ref< bool(index_type, function_ref< DomItem()>)>)> IteratorFunction
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
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)
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.
static QMap< LookupType, QString > lookupTypeToStringMap()
QMLDOM_EXPORT bool domTypeIsContainer(DomType k)
constexpr bool domTypeIsOwningItem(DomType)
static bool visitForLookupType(const DomItem &el, LookupType lookupType, function_ref< bool(const DomItem &)> visitor)
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)
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)
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)
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
#define NewErrorGroup(name)
A common base class for all the script elements.
void setSemanticScope(const QQmlJSScope::ConstPtr &scope)
QQmlJSScope::ConstPtr semanticScope()