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