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