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
qqmldomelements.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
3// Qt-Security score:significant
4
5// Suppress GCC 11 warning about maybe-uninitialized copy of
6// another Data. We're not sure if the compiler is actually right,
7// but in this type of warning, it often isn't.
8//#if defined(Q_CC_GNU) && Q_CC_GNU >= 1100
9//QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
11#include "qqmldompath_p.h"
12#if defined(__GNUC__) && __GNUC__ >= 11
13# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
14#endif
15
19#include "qqmldommock_p.h"
23#include "qqmldomtop_p.h"
25
26#include <QtQml/private/qqmljslexer_p.h>
27#include <QtQml/private/qqmljsparser_p.h>
28#include <QtQml/private/qqmljsengine_p.h>
29#include <QtQml/private/qqmljsastvisitor_p.h>
30#include <QtQml/private/qqmljsast_p.h>
31
32#include <QtCore/QScopeGuard>
33#include <QtCore/QRegularExpression>
34#include <QtCore/QDir>
35#include <QtCore/QBasicMutex>
36#include <QtCore/QUrl>
37
38#include <optional>
39#include <limits>
40
42
43using namespace Qt::StringLiterals;
44
45namespace QQmlJS {
46namespace Dom {
47
48namespace Paths {
49
50Path moduleIndexPath(const QString &uri, int majorVersion, const ErrorHandler &errorHandler)
51{
52 QString version = QString::number(majorVersion);
53 if (majorVersion == Version::Latest)
54 version = QLatin1String("Latest");
55 else if (majorVersion == Version::Undefined)
56 version = QString();
57 QRegularExpression moduleRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
58 auto m = moduleRe.match(uri);
59 if (!m.isValid())
60 Path::myErrors()
61 .error(Path::tr("Invalid module name in import %1").arg(uri))
62 .handle(errorHandler);
63 return Path::fromRoot(PathRoot::Env).withField(Fields::moduleIndexWithUri).withKey(uri).withKey(version);
64}
65
66Path moduleScopePath(const QString &uri, Version version, const ErrorHandler &)
67{
69 .withField(Fields::moduleIndexWithUri)
70 .withKey(uri)
71 .withKey(version.majorSymbolicString())
72 .withField(Fields::moduleScope)
73 .withKey(version.minorString());
74}
75
76Path moduleScopePath(const QString &uri, const QString &version, const ErrorHandler &errorHandler)
77{
78 Version v = Version::fromString(version);
79 if (!version.isEmpty() && !(v.isValid() || v.isLatest()))
80 Path::myErrors().error(Path::tr("Invalid Version %1").arg(version)).handle(errorHandler);
81 return moduleScopePath(uri, v, errorHandler);
82}
83
84} // end namespace Paths
85
87{
88 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Parsing") } };
89 return res;
90}
91
92bool CommentableDomElement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
93{
94 bool cont = true;
95 cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
96 return cont;
97}
98
99void Component::updatePathFromOwner(const Path &newPath)
100{
102 updatePathFromOwnerMultiMap(m_enumerations, newPath.withField(Fields::enumerations));
103 updatePathFromOwnerQList(m_objects, newPath.withField(Fields::objects));
104}
105
107
108Component::Component(const Path &pathFromOwner) : CommentableDomElement(pathFromOwner) { }
109
110bool Component::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
111{
112 bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
113 cont = cont && self.dvValueField(visitor, Fields::name, name());
114 cont = cont && self.dvWrapField(visitor, Fields::enumerations, m_enumerations);
115 cont = cont && self.dvWrapField(visitor, Fields::objects, m_objects);
116 cont = cont && self.dvValueField(visitor, Fields::isSingleton, isSingleton());
117 cont = cont && self.dvValueField(visitor, Fields::isCreatable, isCreatable());
118 cont = cont && self.dvValueField(visitor, Fields::isComposite, isComposite());
119 cont = cont && self.dvValueField(visitor, Fields::attachedTypeName, attachedTypeName());
120 cont = cont && self.dvReferenceField(visitor, Fields::attachedType, attachedTypePath(self));
121 return cont;
122}
123
124DomItem Component::field(const DomItem &self, QStringView name) const
125{
126 if (name == Fields::name)
127 return self.wrapField(Fields::name, m_name);
128 if (name == Fields::objects)
129 return self.wrapField(Fields::objects, m_objects);
130
131 return DomBase::field(self, name);
132}
133
135{
136 return appendUpdatableElementInQList(pathFromOwner().withField(Fields::objects), m_objects, object,
137 oPtr);
138}
139
140bool QmlComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
141{
142 bool cont = Component::iterateDirectSubpaths(self, visitor);
143 cont = cont && self.dvWrapField(visitor, Fields::ids, m_ids);
144 cont = cont && self.dvValueLazyField(visitor, Fields::subComponents, [this, &self]() {
145 return this->subComponents(self);
146 });
147 if (m_nameIdentifiers) {
148 cont = cont && self.dvItemField(visitor, Fields::nameIdentifiers, [this, &self]() {
149 return self.subScriptElementWrapperItem(m_nameIdentifiers);
150 });
151 }
152 return cont;
153}
154
155void QmlComponent::updatePathFromOwner(const Path &newPath)
156{
158 updatePathFromOwnerMultiMap(m_ids, newPath.withField(Fields::annotations));
159}
160
161void QmlComponent::writeOut(const DomItem &self, OutWriter &lw) const
162{
163 if (name().contains(QLatin1Char('.'))) {
164 // inline component
165 const auto fLoc = FileLocations::treeOf(self);
166 lw.ensureNewline()
167 .writeRegion(fLoc, ComponentKeywordRegion)
168 .ensureSpace()
169 .writeRegion(fLoc, IdentifierRegion, name().split(QLatin1Char('.')).last())
170 .writeRegion(fLoc, ColonTokenRegion)
171 .ensureSpace();
172 }
173 self.field(Fields::objects).index(0).writeOut(lw);
174}
175
176QList<QString> QmlComponent::subComponentsNames(const DomItem &self) const
177{
178 DomItem components = self.owner().field(Fields::components);
179 const QSet<QString> cNames = components.keys();
180 QString myNameDot = self.pathFromOwner()[1].headName();
181 if (!myNameDot.isEmpty())
182 myNameDot += QLatin1Char('.');
183 QList<QString> subNames;
184 for (const QString &cName : cNames)
185 if (cName.startsWith(myNameDot)
186 && !QStringView(cName).mid(myNameDot.size()).contains(QLatin1Char('.'))
187 && !cName.isEmpty())
188 subNames.append(cName);
189 std::sort(subNames.begin(), subNames.end());
190 return subNames;
191}
192
193QList<DomItem> QmlComponent::subComponents(const DomItem &self) const
194{
195 DomItem components = self.owner().field(Fields::components);
196 QList<DomItem> res;
197 for (const QString &cName : subComponentsNames(self))
198 for (const DomItem &comp : components.key(cName).values())
199 res.append(comp);
200 return res;
201}
202
203Version Version::fromString(QStringView v)
204{
205 if (v.isEmpty())
206 return Version(Latest, Latest);
207 QRegularExpression r(
208 QRegularExpression::anchoredPattern(QStringLiteral(uR"(([0-9]*)(?:\.([0-9]*))?)")));
209 auto m = r.matchView(v);
210 if (m.hasMatch()) {
211 bool ok;
212 int majorV = m.capturedView(1).toInt(&ok);
213 if (!ok)
214 majorV = Version::Undefined;
215 int minorV = m.capturedView(2).toInt(&ok);
216 if (!ok)
217 minorV = Version::Undefined;
218 return Version(majorV, minorV);
219 }
220 return {};
221}
222
223Version::Version(qint32 majorV, qint32 minorV) : majorVersion(majorV), minorVersion(minorV) { }
224
225bool Version::isLatest() const
226{
227 return majorVersion == Latest && minorVersion == Latest;
228}
229
230bool Version::isValid() const
231{
232 return majorVersion >= 0 && minorVersion >= 0;
233}
234
236{
237 if (isLatest())
238 return QString();
239 if (minorVersion < 0) {
240 if (majorVersion < 0)
241 return QLatin1String(".");
242 else
243 return QString::number(majorVersion);
244 }
245 if (majorVersion < 0)
246 return QLatin1String(".") + QString::number(minorVersion);
247 return QString::number(majorVersion) + QChar::fromLatin1('.') + QString::number(minorVersion);
248}
249
250bool Version::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
251{
252 bool cont = true;
253 cont = cont && self.dvWrapField(visitor, Fields::majorVersion, majorVersion);
254 cont = cont && self.dvWrapField(visitor, Fields::minorVersion, minorVersion);
255 cont = cont && self.dvValueField(visitor, Fields::isLatest, isLatest());
256 cont = cont && self.dvValueField(visitor, Fields::isValid, isValid());
257 cont = cont && self.dvValueLazyField(visitor, Fields::stringValue, [this]() {
258 return this->stringValue();
259 });
260 return cont;
261}
262
263QRegularExpression Import::importRe()
264{
265 static QRegularExpression res(QRegularExpression::anchoredPattern(QStringLiteral(
266 uR"((?<uri>\w+(?:\.\w+)*)(?:\W+(?<version>[0-9]+(?:\.[0-9]*)?))?(?:\W+as\W+(?<id>\w+))?$)")));
267 return res;
268}
269
270Import Import::fromUriString(
271 const QString &importStr, Version v, const QString &importId, const ErrorHandler &handler)
272{
273 auto m = importRe().match(importStr);
274 if (m.hasMatch()) {
275 if (v.majorVersion == Version::Undefined && v.minorVersion == Version::Undefined)
276 v = Version::fromString(m.captured(2));
277 else if (!m.captured(u"version").isEmpty())
279 .warning(tr("Version %1 in import string '%2' overridden by explicit "
280 "version %3")
281 .arg(m.captured(2), importStr, v.stringValue()))
282 .handle(handler);
283 QString resolvedImportId;
284 if (importId.isEmpty()) {
285 resolvedImportId = m.captured(u"importId");
286 } else {
287 if (!m.captured(u"importId").isEmpty()) {
289 .warning(tr("namespace %1 in import string '%2' overridden by explicit "
290 "importId %3")
291 .arg(m.captured(u"importId"), importStr, importId))
292 .handle(handler);
293 }
294 resolvedImportId = importId;
295 }
296
297 return Import(QmlUri::fromUriString(m.captured(u"uri").trimmed()), v, resolvedImportId);
298 }
300 .error(tr("Unexpected URI format in import '%1'").arg(importStr))
301 .handle(handler);
302 return Import();
303}
304
305Import Import::fromFileString(
306 const QString &importStr, const QString &importId, const ErrorHandler &)
307{
308 return Import(QmlUri::fromDirectoryString(importStr), Version(), importId);
309}
310
311bool Import::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
312{
313 bool cont = true;
314 cont = cont && self.dvValueField(visitor, Fields::uri, uri.toString());
315 cont = cont && self.dvWrapField(visitor, Fields::version, version);
316 if (!importId.isEmpty())
317 cont = cont && self.dvValueField(visitor, Fields::importId, importId);
318 if (implicit)
319 cont = cont && self.dvValueField(visitor, Fields::implicit, implicit);
320 cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
321 return cont;
322}
323
324void Import::writeOut(const DomItem &self, OutWriter &ow) const
325{
326 if (implicit)
327 return;
328
329 QString code;
330 const DomItem owner = self.owner();
331 if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
332 code = qmlFilePtr->code();
333
334 // check for an empty line before the import, and preserve it
335 int preNewlines = 0;
336
337 const FileLocations::Tree fLoc = FileLocations::treeOf(self);
338
339 quint32 start = fLoc->info().fullRegion.offset;
340 if (size_t(code.size()) >= start) {
341 while (start != 0) {
342 QChar c = code.at(--start);
343 if (c == u'\n') {
344 if (++preNewlines == 2)
345 break;
346 } else if (!c.isSpace())
347 break;
348 }
349 }
350 if (preNewlines == 0)
351 ++preNewlines;
352
353 ow.ensureNewline(preNewlines);
354 ow.writeRegion(fLoc, ImportTokenRegion).ensureSpace();
355 ow.writeRegion(fLoc, ImportUriRegion, uri.toString());
356 if (uri.isModule()) {
357 QString vString = version.stringValue();
358 if (!vString.isEmpty())
359 ow.ensureSpace().writeRegion(fLoc, VersionRegion, vString);
360 }
361 if (!importId.isEmpty()) {
362 ow.ensureSpace()
363 .writeRegion(fLoc, AsTokenRegion)
364 .ensureSpace()
365 .writeRegion(fLoc, IdNameRegion, importId);
366 }
367}
368
369Id::Id(const QString &idName, const Path &referredObject) : name(idName), referredObjectPath(referredObject) { }
370
371bool Id::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
372{
373 bool cont = true;
374 cont = cont && self.dvValueField(visitor, Fields::name, name);
375 cont = cont && self.dvReferenceField(visitor, Fields::referredObject, referredObjectPath);
376 cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
377 cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
378 cont = cont && self.dvWrapField(visitor, Fields::value, value);
379 return cont;
380}
381
382void Id::updatePathFromOwner(const Path &newPath)
383{
384 updatePathFromOwnerQList(annotations, newPath.withField(Fields::annotations));
385}
386
387Path Id::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
388{
389 return appendUpdatableElementInQList(selfPathFromOwner.withField(Fields::annotations), annotations,
390 annotation, aPtr);
391}
392
393QmlObject::QmlObject(const Path &pathFromOwner) : CommentableDomElement(pathFromOwner) { }
394
395bool QmlObject::iterateBaseDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
396{
397 bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
398 if (!idStr().isEmpty())
399 cont = cont && self.dvValueField(visitor, Fields::idStr, idStr());
400 cont = cont && self.dvValueField(visitor, Fields::name, name());
401 if (!prototypePaths().isEmpty())
402 cont = cont && self.dvReferencesField(visitor, Fields::prototypes, m_prototypePaths);
403 if (nextScopePath())
404 cont = cont && self.dvReferenceField(visitor, Fields::nextScope, nextScopePath());
405 cont = cont && self.dvWrapField(visitor, Fields::propertyDefs, m_propertyDefs);
406 cont = cont && self.dvWrapField(visitor, Fields::bindings, m_bindings);
407 cont = cont && self.dvWrapField(visitor, Fields::methods, m_methods);
408 cont = cont && self.dvWrapField(visitor, Fields::children, m_children);
409 cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
410 cont = cont && self.dvItemField(visitor, Fields::propertyInfos, [this, &self]() {
411 return self.subMapItem(Map(
412 pathFromOwner().withField(Fields::propertyInfos),
413 [&self](const DomItem &map, const QString &k) {
414 auto pInfo = self.propertyInfoWithName(k);
415 return map.wrap(PathEls::Key(k), pInfo);
416 },
417 [&self](const DomItem &) { return self.propertyInfoNames(); },
418 QLatin1String("PropertyInfo")));
419 });
420 if (m_nameIdentifiers) {
421 cont = cont && self.dvItemField(visitor, Fields::nameIdentifiers, [this, &self]() {
422 return self.subScriptElementWrapperItem(m_nameIdentifiers);
423 });
424 }
425 return cont;
426}
427
429{
430 static QList<QString> myFields(
431 { Fields::comments.toString(), Fields::idStr.toString(),
432 Fields::name.toString(), Fields::prototypes.toString(),
433 Fields::nextScope.toString(), Fields::propertyDefs.toString(),
434 Fields::bindings.toString(), Fields::methods.toString(),
435 Fields::children.toString(), Fields::annotations.toString(),
436 Fields::propertyInfos.toString() });
437 return myFields;
438}
439
440bool QmlObject::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
441{
442 bool cont = iterateBaseDirectSubpaths(self, visitor);
443 cont = cont && self.dvValueLazyField(visitor, Fields::defaultPropertyName, [this, &self]() {
444 return defaultPropertyName(self);
445 });
446 return cont;
447}
448
449DomItem QmlObject::field(const DomItem &self, QStringView name) const
450{
451 if (name == Fields::name)
452 return self.subDataItem(PathEls::Field(Fields::name), this->name());
453 if (name == Fields::idStr) {
454 if (idStr().isEmpty())
455 return DomItem();
456 return self.subDataItem(PathEls::Field(Fields::idStr), idStr());
457 }
458 if (name == Fields::methods)
459 return self.wrapField(Fields::methods, m_methods);
460 if (name == Fields::bindings)
461 return self.wrapField(Fields::bindings, m_bindings);
462 if (name == Fields::comments)
463 return CommentableDomElement::field(self, name);
464 if (name == Fields::children)
465 return self.wrapField(Fields::children, m_children);
466
467 if (name == Fields::nextScope) {
468 if (nextScopePath())
469 return self.subReferenceItem(PathEls::Field(Fields::nextScope), nextScopePath());
470 else
471 return DomItem();
472 }
473 if (name == Fields::prototypes) {
474 if (prototypePaths().isEmpty())
475 return DomItem();
476 return self.subReferencesItem(PathEls::Field(Fields::prototypes), m_prototypePaths);
477 }
478 if (name == Fields::annotations)
479 return self.wrapField(Fields::annotations, m_annotations);
480 if (name == Fields::propertyDefs)
481 return self.wrapField(Fields::propertyDefs, m_propertyDefs);
482 if (name == Fields::propertyInfos) {
483 // Need to explicitly copy self here since we might store this and call it later.
484 return self.subMapItem(Map(
485 pathFromOwner().withField(Fields::propertyInfos),
486 [copiedSelf = self](const DomItem &map, const QString &k) {
487 return map.wrap(PathEls::Key(k), copiedSelf.propertyInfoWithName(k));
488 },
489 [copiedSelf = self](const DomItem &) { return copiedSelf.propertyInfoNames(); },
490 QLatin1String("PropertyInfo")));
491 }
492 if (name == Fields::nameIdentifiers && m_nameIdentifiers) {
493 return self.subScriptElementWrapperItem(m_nameIdentifiers);
494 }
495 if (name == Fields::defaultPropertyName) {
496 return self.subDataItem(PathEls::Field(Fields::defaultPropertyName),
497 defaultPropertyName(self));
498 }
499 static QStringList knownLookups({ Fields::fileLocationsTree.toString() });
500 if (!knownLookups.contains(name)) {
501 qCWarning(domLog()) << "Asked non existing field " << name << " in QmlObject "
502 << pathFromOwner();
503 }
504 return DomItem();
505}
506
507void QmlObject::updatePathFromOwner(const Path &newPath)
508{
510 updatePathFromOwnerMultiMap(m_propertyDefs, newPath.withField(Fields::propertyDefs));
511 updatePathFromOwnerMultiMap(m_bindings, newPath.withField(Fields::bindings));
512 updatePathFromOwnerMultiMap(m_methods, newPath.withField(Fields::methods));
513 updatePathFromOwnerQList(m_children, newPath.withField(Fields::children));
514 updatePathFromOwnerQList(m_annotations, newPath.withField(Fields::annotations));
515}
516
518{
519 if (!m_defaultPropertyName.isEmpty())
520 return m_defaultPropertyName;
521 for (const PropertyDefinition &pDef : m_propertyDefs)
522 if (pDef.isDefaultMember)
523 return pDef.name;
524 return QString();
525}
526
528{
529 QString dProp = localDefaultPropertyName();
530 if (!dProp.isEmpty())
531 return dProp;
532 QString res = QStringLiteral(u"data");
533 self.visitPrototypeChain(
534 [&res](const DomItem &obj) {
535 if (const QmlObject *objPtr = obj.as<QmlObject>()) {
536 QString dProp = objPtr->localDefaultPropertyName();
537 if (!dProp.isEmpty()) {
538 res = dProp;
539 return false;
540 }
541 }
542 return true;
543 },
545 return res;
546}
547
548bool QmlObject::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &)> visitor) const
549{
550 bool cont = self.field(Fields::bindings).visitKeys([visitor](const QString &, const DomItem &bs) {
551 return bs.visitIndexes([visitor](const DomItem &b) {
552 DomItem v = b.field(Fields::value);
553 if (std::shared_ptr<ScriptExpression> vPtr = v.ownerAs<ScriptExpression>()) {
554 if (!visitor(v))
555 return false;
556 return v.iterateSubOwners(visitor); // currently not needed, avoid?
557 }
558 return true;
559 });
560 });
561 cont = cont && self.field(Fields::children).visitIndexes([visitor](const DomItem &qmlObj) {
562 if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>()) {
563 return qmlObjPtr->iterateSubOwners(qmlObj, visitor);
564 }
565 Q_ASSERT(false);
566 return true;
567 });
568 return cont;
569}
570
571static QStringList dotExpressionToList(const std::shared_ptr<ScriptExpression> &expr)
572{
573 QStringList res;
574 AST::Node *node = (expr ? expr->ast() : nullptr);
575 while (node) {
576 switch (node->kind) {
577 case AST::Node::Kind_IdentifierExpression: {
578 AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(node);
579 res.prepend(id->name.toString());
580 return res;
581 }
582 case AST::Node::Kind_FieldMemberExpression: {
583 AST::FieldMemberExpression *id = AST::cast<AST::FieldMemberExpression *>(node);
584 res.prepend(id->name.toString());
585 node = id->base;
586 break;
587 }
588 default:
589 qCDebug(writeOutLog).noquote() << "Could not convert dot expression to list for:\n"
590 << expr->astRelocatableDump();
591 return QStringList();
592 }
593 }
594 return res;
595}
596
598 std::shared_ptr<ScriptExpression> accessSequence) const
599{
600 QStringList accessSequenceList = dotExpressionToList(accessSequence);
601 return resolveAlias(self, accessSequenceList);
602}
603
604LocallyResolvedAlias QmlObject::resolveAlias(const DomItem &self, const QStringList &accessSequence) const
605{
607 QSet<QString> visitedAlias;
608 if (accessSequence.isEmpty()) {
609 return res;
610 } else if (accessSequence.size() > 3) {
612 return res;
613 }
614 QString idName = accessSequence.first();
615 DomItem idTarget = self.component()
616 .field(Fields::ids)
617 .key(idName)
618 .index(0)
619 .field(Fields::referredObject)
620 .get();
621 if (!idTarget)
622 return res;
623 res.baseObject = idTarget;
624 res.accessedPath = accessSequence.mid(1);
625 res.typeName = idTarget.name();
627 // check if it refers to locally defined props/objs
628 while (!res.accessedPath.isEmpty()) {
629 QString pNow = res.accessedPath.first();
630 DomItem defNow = res.baseObject.propertyDefs().key(pNow).index(0);
631 if (const PropertyDefinition *defNowPtr = defNow.as<PropertyDefinition>()) {
632 if (defNowPtr->isAlias()) {
633 res.typeName = QString();
634 ++res.nAliases;
635 QString aliasPath = defNow.canonicalPath().toString();
636 if (visitedAlias.contains(aliasPath)) {
638 return res;
639 }
640 visitedAlias.insert(aliasPath);
641 DomItem valNow = res.baseObject.bindings().key(pNow).index(0);
642 if (std::shared_ptr<ScriptExpression> exp =
643 valNow.field(Fields::value).ownerAs<ScriptExpression>()) {
644 QStringList expList = dotExpressionToList(exp);
645 if (expList.isEmpty()) {
647 return res;
648 } else if (expList.size() > 3) {
650 return res;
651 }
652 idName = expList.first();
653 idTarget = self.component()
654 .field(Fields::ids)
655 .key(idName)
656 .index(0)
657 .field(Fields::referredObject)
658 .get();
659 res.baseObject = idTarget;
660 res.accessedPath = expList.mid(1) + res.accessedPath.mid(1);
661 if (!idTarget) {
663 return res;
664 }
666 res.typeName = idTarget.name();
667 } else {
669 return res;
670 }
671 } else {
672 res.localPropertyDef = defNow;
673 res.typeName = defNowPtr->typeName;
674 res.accessedPath = res.accessedPath.mid(1);
675 DomItem valNow = res.baseObject.bindings().key(pNow).index(0).field(Fields::value);
676 if (valNow.internalKind() == DomType::QmlObject) {
677 res.baseObject = valNow;
678 res.typeName = valNow.name();
680 } else {
682 return res;
683 }
684 }
685 } else {
686 return res;
687 }
688 }
689 return res;
690}
691
693 MutableDomItem &self, const PropertyDefinition &propertyDef, AddOption option)
694{
695 Path p = addPropertyDef(propertyDef, option);
696 if (p.last().headIndex(0) > 1)
697 self.owningItemPtr()->addErrorLocal(domParsingErrors().error(
698 tr("Repeated PropertyDefinition with name %1").arg(propertyDef.name)));
699 return self.owner().path(p);
700}
701
703{
704 Path p = addBinding(binding, option);
705 if (p && p.last().headIndex(0) > 1)
706 self.owningItemPtr()->addErrorLocal(
707 domParsingErrors().error(tr("Repeated binding with name %1").arg(binding.name())));
708 return self.owner().path(p);
709}
710
712 MutableDomItem &self, const MethodInfo &functionDef, AddOption option)
713{
714 Path p = addMethod(functionDef, option);
715 if (p.last().headIndex(0) > 1)
716 self.owningItemPtr()->addErrorLocal(
717 domParsingErrors().error(tr("Repeated Method with name %1").arg(functionDef.name)));
718 return self.owner().path(p);
719}
720
721void QmlObject::writeOutId(const DomItem &self, OutWriter &ow) const
722{
723 if (!idStr().isEmpty()) { // *always* put id first
724 DomItem myId = self.component().field(Fields::ids).key(idStr()).index(0);
725 myId.writeOutPre(ow);
726 const auto fLoc = FileLocations::treeOf(myId);
727 ow.ensureNewline()
728 .writeRegion(fLoc, IdTokenRegion)
729 .writeRegion(fLoc, IdColonTokenRegion)
730 .ensureSpace()
731 .writeRegion(fLoc, IdNameRegion, idStr());
732 if (ow.lineWriter.options().attributesSequence
733 == LineWriterOptions::AttributesSequence::Normalize) {
734 ow.ensureNewline(2);
735 }
736 if (myId) {
737 myId.writeOutPost(ow);
738 ow.ensureNewline(1);
739 }
740 }
741}
742
744 const DomItem &component) const
745{
746 auto startLoc = [&](const FileLocations::Tree &l) {
747 if (l)
748 return l->info().fullRegion;
749 return SourceLocation(posOfNewElements, 0, 0, 0);
750 };
751
752 QList<std::pair<SourceLocation, DomItem>> attribs;
753 const auto objLocPtr = FileLocations::treeOf(self);
754 FileLocations::Tree componentLoc;
755 if (component && objLocPtr)
756 componentLoc = objLocPtr->parent()->parent();
757 auto addMMap = [&attribs, &startLoc](const DomItem &base, const FileLocations::Tree &baseLoc) {
758 if (!base)
759 return;
760 const auto values = base.values();
761 for (const auto &els : values) {
762 FileLocations::Tree elsLoc = FileLocations::find(baseLoc, els.pathFromOwner().last());
763 const auto elsValues = els.values();
764 for (const auto &el : elsValues) {
765 FileLocations::Tree elLoc = FileLocations::find(elsLoc, el.pathFromOwner().last());
766 attribs.append(std::make_pair(startLoc(elLoc), el));
767 }
768 }
769 };
770 auto addMyMMap = [this, &objLocPtr, &self, &addMMap](QStringView fieldName) {
771 DomItem base = this->field(self, fieldName);
772 addMMap(base, FileLocations::find(objLocPtr, base.pathFromOwner().last()));
773 };
774 auto addSingleLevel = [&attribs, &startLoc](const DomItem &base,
775 const FileLocations::Tree &baseLoc) {
776 if (!base)
777 return;
778 const auto baseValues = base.values();
779 for (const auto &el : baseValues) {
780 FileLocations::Tree elLoc = FileLocations::find(baseLoc, el.pathFromOwner().last());
781 attribs.append(std::make_pair(startLoc(elLoc), el));
782 }
783 };
784 if (component) {
785 DomItem enums = component.field(Fields::enumerations);
786 addMMap(enums, FileLocations::find(componentLoc, enums.pathFromOwner().last()));
787 }
788 addMyMMap(Fields::propertyDefs);
789 addMyMMap(Fields::bindings);
790 addMyMMap(Fields::methods);
791 DomItem children = field(self, Fields::children);
792 addSingleLevel(children, FileLocations::find(objLocPtr, children.pathFromOwner().last()));
793 if (component) {
794 DomItem subCs = component.field(Fields::subComponents);
795 for (const DomItem &c : subCs.values()) {
796 const auto subLocPtr = FileLocations::treeOf(c);
797 Q_ASSERT(subLocPtr);
798 attribs.append(std::make_pair(startLoc(subLocPtr), c));
799 }
800 }
801 std::stable_sort(attribs.begin(), attribs.end(),
802 [](const std::pair<SourceLocation, DomItem> &el1,
803 const std::pair<SourceLocation, DomItem> &el2) {
804 if (el1.first.offset < el2.first.offset)
805 return true;
806 if (el1.first.offset > el2.first.offset)
807 return false;
808 int i = int(el1.second.internalKind()) - int(el2.second.internalKind());
809 return i < 0;
810 });
811 return attribs;
812}
813
814void QmlObject::writeOutAttributes(const DomItem &self, OutWriter &ow, const DomItem &component,
815 const QString &code) const
816{
817 const QList<std::pair<SourceLocation, DomItem>> attribs = orderOfAttributes(self, component);
818 qsizetype iAttr = 0;
819 while (iAttr != attribs.size()) {
820 auto &el = attribs[iAttr++];
821 // check for an empty line before the current element, and preserve it
822 int preNewlines = 0;
823 quint32 start = el.first.offset;
824 if (start != posOfNewElements && size_t(code.size()) >= start) {
825 while (start != 0) {
826 QChar c = code.at(--start);
827 if (c == u'\n') {
828 if (++preNewlines == 2)
829 break;
830 } else if (!c.isSpace())
831 break;
832 }
833 }
834 if (preNewlines == 0)
835 ++preNewlines;
836 ow.ensureNewline(preNewlines);
837 if (el.second.internalKind() == DomType::PropertyDefinition && iAttr != attribs.size()
838 && el.first.offset != ~quint32(0)) {
839 DomItem b;
840 auto &bPair = attribs[iAttr];
841 if (bPair.second.internalKind() == DomType::Binding
842 && bPair.first.begin() < el.first.end()
843 && bPair.second.name() == el.second.name()) {
844 b = bPair.second;
845 ++iAttr;
847 }
848 el.second.writeOut(ow);
849 if (b) {
850 const auto fLoc = FileLocations::treeOf(b);
851 ow.writeRegion(fLoc, ColonTokenRegion);
852 ow.ensureSpace();
853 if (const Binding *bPtr = b.as<Binding>())
854 bPtr->writeOutValue(b, ow);
855 else {
856 qWarning() << "Internal error casting binding to Binding in"
858 ow.writeRegion(fLoc, LeftBraceRegion).writeRegion(fLoc, RightBraceRegion);
859 }
861 }
862 } else {
863 el.second.writeOut(ow);
864 }
865 ow.ensureNewline();
866 }
867}
868
869void QmlObject::writeOutSortedEnumerations(const DomItem &component, OutWriter &ow) const
870{
871 const auto descs = component.field(Fields::enumerations).values();
872 for (const auto &enumDescs : descs) {
873 const auto values = enumDescs.values();
874 for (const auto &enumDesc : values) {
875 ow.ensureNewline(1);
876 enumDesc.writeOut(ow);
877 ow.ensureNewline(1);
878 }
879 }
880}
881
882void QmlObject::writeOutSortedPropertyDefinition(const DomItem &self, OutWriter &ow,
883 QSet<QString> &mergedDefBinding) const
884{
885 DomItem propertyDefs = field(self, Fields::propertyDefs);
886 DomItem bindings = field(self, Fields::bindings);
887
888 for (const QString &defName : propertyDefs.sortedKeys()) {
889 const auto pDefs = propertyDefs.key(defName).values();
890 for (const auto &pDef : pDefs) {
891 const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>();
892 Q_ASSERT(pDefPtr);
893 DomItem b;
894 bool uniqueDeclarationWithThisName = pDefs.size() == 1;
895 if (uniqueDeclarationWithThisName && !pDefPtr->isRequired)
896 bindings.key(pDef.name()).visitIndexes([&b, pDefPtr](const DomItem &el) {
897 const Binding *elPtr = el.as<Binding>();
898 if (elPtr && elPtr->bindingType() == BindingType::Normal) {
899 switch (elPtr->valueKind()) {
900 case BindingValueKind::ScriptExpression:
901 b = el;
902 break;
903 case BindingValueKind::Array:
904 if (!pDefPtr->isDefaultMember && pDefPtr->isParametricType())
905 b = el;
906 break;
907 case BindingValueKind::Object:
908 if (!pDefPtr->isDefaultMember && !pDefPtr->isParametricType())
909 b = el;
910 break;
911 case BindingValueKind::Empty:
912 break;
913 }
914 return false;
915 }
916 return true;
917 });
918 if (b) {
919 mergedDefBinding.insert(defName);
920 b.writeOutPre(ow);
921 }
922 pDef.writeOut(ow);
923 if (b) {
924 const auto fLoc = FileLocations::treeOf(b);
925 ow.writeRegion(fLoc, ColonTokenRegion);
926 ow.ensureSpace();
927 if (const Binding *bPtr = b.as<Binding>())
928 bPtr->writeOutValue(b, ow);
929 else {
930 qWarning() << "Internal error casting binding to Binding in"
931 << b.canonicalPath();
932 ow.writeRegion(fLoc, LeftBraceRegion).writeRegion(fLoc, RightBraceRegion);
933 }
934 b.writeOutPost(ow);
935 }
936 }
937 }
938}
939
941{
943 const auto fields = methods.values();
944 for (const auto &ms : fields) {
945 const auto values = ms.values();
946 for (const auto &m : values) {
947 const MethodInfo *mPtr = m.as<MethodInfo>();
950 else
952 }
953 }
955}
956
959{
961 for (const auto &bName : bindings.sortedKeys()) {
963 const auto values = bindings.key(bName).values();
964 for (const auto &b : values) {
965 const Binding *bPtr = b.as<Binding>();
966 if (skipFirstNormal) {
967 if (bPtr && bPtr->bindingType() == BindingType::Normal) {
968 skipFirstNormal = false;
969 continue;
970 }
971 }
975 else if (b.field(Fields::isSignalHandler).value().toBool(false))
977 else
979 }
980 }
982}
983
984void QmlObject::writeOutSortedAttributes(const DomItem &self, OutWriter &ow,
985 const DomItem &component) const
986{
987 int spacerId = 0;
988 quint32 counter = ow.counter();
989
990 if (component)
992
993 if (counter != ow.counter() || !idStr().isEmpty())
994 spacerId = ow.addNewlinesAutospacerCallback(2);
995
996 QSet<QString> mergedDefBinding;
997 writeOutSortedPropertyDefinition(self, ow, mergedDefBinding);
998
999 ow.removeTextAddCallback(spacerId);
1000 if (counter != ow.counter())
1001 spacerId = ow.addNewlinesAutospacerCallback(2);
1002
1003 const auto [signalList, methodList] = splitSignalsAndMethods(field(self, Fields::methods));
1004 for (const auto &sig : std::as_const(signalList)) {
1005 ow.ensureNewline();
1006 sig.writeOut(ow);
1007 ow.ensureNewline();
1008 }
1009
1010 ow.removeTextAddCallback(spacerId);
1011 if (counter != ow.counter())
1012 spacerId = ow.addNewlinesAutospacerCallback(2);
1013
1014 bool first = true;
1015 for (const auto &method : std::as_const(methodList)) {
1016 if (!first && ow.lineWriter.options().functionsSpacing) {
1017 ow.newline();
1018 }
1019 ow.ensureNewline();
1020 first = false;
1021 method.writeOut(ow);
1022 ow.ensureNewline();
1023 }
1024 ow.removeTextAddCallback(spacerId);
1025
1026 DomItem bindings = field(self, Fields::bindings);
1027 const auto [normalBindings, signalHandlers, delayedBindings] =
1028 splitBindings(bindings, mergedDefBinding);
1029
1030 if (counter != ow.counter())
1031 spacerId = ow.addNewlinesAutospacerCallback(2);
1032 for (const auto &b : std::as_const(normalBindings))
1033 b.writeOut(ow);
1034 ow.removeTextAddCallback(spacerId);
1035
1036 if (counter != ow.counter())
1037 spacerId = ow.addNewlinesAutospacerCallback(2);
1038 for (const auto &b : std::as_const(delayedBindings))
1039 b.writeOut(ow);
1040 ow.removeTextAddCallback(spacerId);
1041
1042 if (counter != ow.counter())
1043 spacerId = ow.addNewlinesAutospacerCallback(2);
1044 for (const auto &b : std::as_const(signalHandlers))
1045 b.writeOut(ow);
1046 ow.removeTextAddCallback(spacerId);
1047
1048 if (counter != ow.counter())
1049 spacerId = ow.addNewlinesAutospacerCallback(2);
1050 first = true;
1051
1052 const auto values = field(self, Fields::children).values();
1053 for (const auto &c : values) {
1054 if (!first && ow.lineWriter.options().objectsSpacing) {
1055 ow.newline().newline();
1056 }
1057 first = false;
1058 ow.ensureNewline();
1059 c.writeOut(ow);
1060 }
1061 ow.removeTextAddCallback(spacerId);
1062
1063 if (component) {
1064 // we are a root object, possibly add components
1065 DomItem subComps = component.field(Fields::subComponents);
1066 if (counter != ow.counter())
1067 spacerId = ow.addNewlinesAutospacerCallback(2);
1068 const auto values = subComps.values();
1069 for (const auto &subC : values) {
1070 ow.ensureNewline();
1071 subC.writeOut(ow);
1072 }
1073 ow.removeTextAddCallback(spacerId);
1074 }
1075 if (counter != ow.counter() || !ow.lineWriter.options().singleLineEmptyObjects)
1076 ow.ensureNewline();
1077}
1078
1079void QmlObject::writeOut(const DomItem &self, OutWriter &ow, const QString &onTarget) const
1080{
1081 bool isRootObject = pathFromOwner().length() == 5
1082 && pathFromOwner()[0] == Path::fromField(Fields::components)
1083 && pathFromOwner()[3] == Path::fromField(Fields::objects);
1084 const auto fLoc = FileLocations::treeOf(self);
1085 ow.writeRegion(fLoc, IdentifierRegion, name());
1086 if (!onTarget.isEmpty()) {
1087 ow.ensureSpace()
1088 .writeRegion(fLoc, OnTokenRegion)
1089 .ensureSpace()
1090 .writeRegion(fLoc, OnTargetRegion, onTarget);
1091 }
1092 ow.ensureSpace();
1093 ow.writeRegion(fLoc, LeftBraceRegion);
1094
1095 // *always* put id first
1096 writeOutId(self, ow);
1097
1098 DomItem component;
1099 if (isRootObject)
1100 component = self.containingObject();
1101 if (ow.lineWriter.options().attributesSequence
1102 == LineWriterOptions::AttributesSequence::Preserve) {
1103 QString code;
1104 if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
1105 code = qmlFilePtr->code();
1106 writeOutAttributes(self, ow, component, code);
1107 } else {
1108 writeOutSortedAttributes(self, ow, component);
1109 }
1110 ow.writeRegion(fLoc, RightBraceRegion);
1111}
1112
1113Binding::Binding(const QString &name) : Binding(name, std::unique_ptr<BindingValue>()) { }
1114
1115Binding::Binding(const QString &name, std::unique_ptr<BindingValue> &&value,
1116 BindingType bindingType)
1117 : m_bindingType(bindingType), m_name(name), m_value(std::move(value))
1118{
1119}
1120
1122 const QString &name, const std::shared_ptr<ScriptExpression> &value,
1123 BindingType bindingType)
1124 : Binding(name, std::make_unique<BindingValue>(value), bindingType)
1125{
1126}
1127
1128Binding::Binding(const QString &name, const QString &scriptCode, BindingType bindingType)
1129 : Binding(name,
1133{
1134}
1135
1136Binding::Binding(const QString &name, const QmlObject &value, BindingType bindingType)
1137 : Binding(name, std::make_unique<BindingValue>(value), bindingType)
1138{
1139}
1140
1141Binding::Binding(const QString &name, const QList<QmlObject> &value, BindingType bindingType)
1142 : Binding(name, std::make_unique<BindingValue>(value), bindingType)
1143{
1144}
1145
1147 : m_bindingType(o.m_bindingType),
1148 m_name(o.m_name),
1152{
1153 if (o.m_value) {
1154 m_value = std::make_unique<BindingValue>(*o.m_value);
1155 }
1156}
1157
1159
1161{
1162 m_name = o.m_name;
1163 m_bindingType = o.m_bindingType;
1164 m_annotations = o.m_annotations;
1165 m_comments = o.m_comments;
1166 m_bindingIdentifiers = o.m_bindingIdentifiers;
1167 if (o.m_value) {
1168 if (!m_value)
1169 m_value = std::make_unique<BindingValue>(*o.m_value);
1170 else
1171 *m_value = *o.m_value;
1172 } else {
1173 m_value.reset();
1174 }
1175 return *this;
1176}
1177
1178bool Binding::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1179{
1180 bool cont = true;
1181 cont = cont && self.dvValueField(visitor, Fields::name, m_name);
1182 cont = cont && self.dvValueField(visitor, Fields::isSignalHandler, isSignalHandler());
1183 if (!m_value)
1184 cont = cont && visitor(PathEls::Field(Fields::value), []() { return DomItem(); });
1185 else
1186 cont = cont && self.dvItemField(visitor, Fields::value, [this, &self]() {
1187 return m_value->value(self);
1188 });
1189 cont = cont && self.dvValueField(visitor, Fields::bindingType, int(m_bindingType));
1190 cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
1191 cont = cont && self.dvValueLazyField(visitor, Fields::preCode, [this]() {
1192 return this->preCode();
1193 });
1194 cont = cont && self.dvValueLazyField(visitor, Fields::postCode, [this]() {
1195 return this->postCode();
1196 });
1197 if (m_bindingIdentifiers) {
1198 cont = cont && self.dvItemField(visitor, Fields::bindingIdentifiers, [this, &self]() {
1199 return self.subScriptElementWrapperItem(m_bindingIdentifiers);
1200 });
1201 }
1202 cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
1203 return cont;
1204}
1205
1207{
1208 if (!m_value)
1209 return DomItem();
1210 return m_value->value(self);
1211}
1212
1214{
1215 if (!m_value)
1216 return BindingValueKind::Empty;
1217 return m_value->kind;
1218}
1219
1221{
1222 if (valueKind() == BindingValueKind::Object)
1223 return &(m_value->object);
1224 return nullptr;
1225}
1226
1228{
1229 if (valueKind() == BindingValueKind::Object)
1230 return &(m_value->object);
1231 return nullptr;
1232}
1233
1235{
1236 if (valueKind() == BindingValueKind::Array)
1237 return &(m_value->array);
1238 return nullptr;
1239}
1240
1242{
1243 if (valueKind() == BindingValueKind::Array)
1244 return &(m_value->array);
1245 return nullptr;
1246}
1247
1249{
1250 if (valueKind() == BindingValueKind::ScriptExpression)
1251 return m_value->scriptExpression;
1252 return nullptr;
1253}
1254
1256{
1257 if (valueKind() == BindingValueKind::ScriptExpression)
1258 return m_value->scriptExpression;
1259 return nullptr;
1260}
1261
1262void Binding::setValue(std::unique_ptr<BindingValue> &&value)
1263{
1264 m_value = std::move(value);
1265}
1266
1267Path Binding::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
1268{
1269 return appendUpdatableElementInQList(selfPathFromOwner.withField(Fields::annotations),
1270 m_annotations, annotation, aPtr);
1271}
1272
1273void Binding::updatePathFromOwner(const Path &newPath)
1274{
1275 Path base = newPath.withField(Fields::annotations);
1276 if (m_value)
1277 m_value->updatePathFromOwner(newPath.withField(Fields::value));
1278 updatePathFromOwnerQList(m_annotations, newPath.withField(Fields::annotations));
1279}
1280
1281void Binding::writeOut(const DomItem &self, OutWriter &lw) const
1282{
1283 const auto fLoc = FileLocations::treeOf(self);
1284 lw.ensureNewline();
1285 if (m_bindingType == BindingType::Normal) {
1286 lw.writeRegion(fLoc, IdentifierRegion, name());
1287 lw.writeRegion(fLoc, ColonTokenRegion).ensureSpace();
1288 writeOutValue(self, lw);
1289 } else {
1290 DomItem v = valueItem(self);
1291 if (const QmlObject *vPtr = v.as<QmlObject>()) {
1292 v.writeOutPre(lw);
1293 vPtr->writeOut(v, lw, name());
1294 v.writeOutPost(lw);
1295 } else {
1296 qCWarning(writeOutLog()) << "On Binding requires an QmlObject Value, not "
1297 << v.internalKindStr() << " at " << self.canonicalPath();
1298 }
1299 }
1300}
1301
1302void Binding::writeOutValue(const DomItem &self, OutWriter &lw) const
1303{
1304 DomItem v = valueItem(self);
1305 const auto fLoc = FileLocations::treeOf(v);
1306 switch (valueKind()) {
1307 case BindingValueKind::Empty:
1308 qCWarning(writeOutLog()) << "Writing of empty binding " << name();
1309 lw.writeRegion(fLoc, LeftBraceRegion);
1310 lw.writeRegion(fLoc, RightBraceRegion);
1311 break;
1312 case BindingValueKind::Array:
1313 if (const List *vPtr = v.as<List>()) {
1314 v.writeOutPre(lw);
1315 vPtr->writeOut(v, lw, false);
1316 v.writeOutPost(lw);
1317 }
1318 break;
1319 case BindingValueKind::Object:
1320 case BindingValueKind::ScriptExpression:
1321 v.writeOut(lw);
1322 break;
1323 }
1324}
1325
1326bool QmltypesComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1327{
1328 bool cont = Component::iterateDirectSubpaths(self, visitor);
1329 cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
1330 cont = cont && self.dvValueField(visitor, Fields::metaRevisions, m_metaRevisions);
1331 if (!fileName().isEmpty())
1332 cont = cont && self.dvValueField(visitor, Fields::fileName, fileName()); // remove?
1333 cont = cont && self.dvValueField(visitor, Fields::interfaceNames, m_interfaceNames);
1334 cont = cont && self.dvValueField(visitor, Fields::hasCustomParser, m_hasCustomParser);
1335 cont = cont && self.dvValueField(visitor, Fields::elementTypeName, m_elementTypeName);
1336 cont = cont && self.dvValueField(visitor, Fields::extensionTypeName, m_extensionTypeName);
1337 cont = cont && self.dvValueField(visitor, Fields::accessSemantics, int(m_accessSemantics));
1338 return cont;
1339}
1340
1341Export Export::fromString(
1342 const Path &source, QStringView exp, const Path &typePath, const ErrorHandler &h)
1343{
1344 Export res;
1345 res.exportSourcePath = source;
1346 res.typePath = typePath;
1347 int slashIdx = exp.indexOf(QLatin1Char('/'));
1348 int spaceIdx = exp.indexOf(QLatin1Char(' '));
1349 if (spaceIdx == -1)
1350 spaceIdx = exp.size();
1351 else
1352 res.version = Version::fromString(exp.mid(spaceIdx + 1));
1353 if (!res.version.isValid())
1355 .error(tr("Expected string literal to contain 'Package/Name major.minor' "
1356 "or 'Name major.minor' not '%1'.")
1357 .arg(exp))
1358 .handle(h);
1359 if (slashIdx != -1)
1360 res.uri = exp.left(slashIdx).toString();
1361 res.typeName = exp.mid(slashIdx + 1, spaceIdx - (slashIdx + 1)).toString();
1362 return res;
1363}
1364
1365bool AttributeInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1366{
1367 bool cont = true;
1368 cont = cont && self.dvValueField(visitor, Fields::name, name);
1369 cont = cont && self.dvValueField(visitor, Fields::access, int(access));
1370 cont = cont && self.dvValueField(visitor, Fields::typeName, typeName);
1371 cont = cont && self.dvValueField(visitor, Fields::isReadonly, isReadonly);
1372 cont = cont && self.dvValueField(visitor, Fields::isList, isList);
1373 cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
1374 cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
1375 return cont;
1376}
1377
1378Path AttributeInfo::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation,
1379 QmlObject **aPtr)
1380{
1381 return appendUpdatableElementInQList(selfPathFromOwner.withField(Fields::annotations), annotations,
1382 annotation, aPtr);
1383}
1384
1386{
1387 Path base = newPath.withField(Fields::annotations);
1388 updatePathFromOwnerQList(annotations, newPath.withField(Fields::annotations));
1389}
1390
1391bool EnumDecl::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1392{
1393 bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
1394 cont = cont && self.dvValueField(visitor, Fields::name, name());
1395 cont = cont && self.dvWrapField(visitor, Fields::values, m_values);
1396 cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
1397 return cont;
1398}
1399
1400void EnumDecl::updatePathFromOwner(const Path &newPath)
1401{
1403 updatePathFromOwnerQList(m_annotations, newPath.withField(Fields::annotations));
1404}
1405
1406void EnumDecl::setAnnotations(const QList<QmlObject> &annotations)
1407{
1408 m_annotations = annotations;
1409}
1410
1411Path EnumDecl::addAnnotation(const QmlObject &annotation, QmlObject **aPtr)
1412{
1413 return appendUpdatableElementInQList(pathFromOwner().withField(Fields::annotations), m_annotations,
1414 annotation, aPtr);
1415}
1416
1417void EnumDecl::writeOut(const DomItem &self, OutWriter &ow) const
1418{
1419 const auto fLoc = FileLocations::treeOf(self);
1420 ow.writeRegion(fLoc, EnumKeywordRegion)
1421 .ensureSpace()
1422 .writeRegion(fLoc, IdentifierRegion, name())
1423 .ensureSpace()
1424 .writeRegion(fLoc, LeftBraceRegion);
1425 const auto values = self.field(Fields::values).values();
1426 for (const auto &value : values)
1427 value.writeOut(ow);
1428 ow.ensureNewline().writeRegion(fLoc, RightBraceRegion);
1429}
1430
1432{
1433 DomItem top = self.top();
1434 DomItem env = top.environment();
1435 Path selfPath = self.canonicalPath().withField(Fields::allSources);
1436 RefCacheEntry cached = (env ? RefCacheEntry::forPath(env, selfPath) : RefCacheEntry());
1437 if (cached.cached == RefCacheEntry::Cached::All)
1438 return cached.canonicalPaths;
1439 QList<Path> res;
1440 QSet<Path> knownPaths;
1441 QList<Path> toDo(m_importSourcePaths.rbegin(), m_importSourcePaths.rend());
1442 while (!toDo.isEmpty()) {
1443 Path pNow = toDo.takeLast();
1444 if (knownPaths.contains(pNow))
1445 continue;
1446 knownPaths.insert(pNow);
1447 res.append(pNow);
1448 DomItem sourceBase = top.path(pNow);
1449 for (const DomItem &autoExp : sourceBase.field(Fields::autoExports).values()) {
1450 if (const ModuleAutoExport *autoExpPtr = autoExp.as<ModuleAutoExport>()) {
1451 Path newSource;
1452 if (autoExpPtr->inheritVersion) {
1453 Version v = autoExpPtr->import.version;
1454 DomItem sourceVersion = sourceBase.field(Fields::version);
1455 if (const Version *sourceVersionPtr = sourceVersion.as<Version>()) {
1456 if (v.majorVersion < 0)
1457 v.majorVersion = sourceVersionPtr->majorVersion;
1458 if (v.minorVersion < 0)
1459 v.minorVersion = sourceVersionPtr->minorVersion;
1460 } else {
1461 qWarning() << "autoExport with inherited version " << autoExp
1462 << " but missing version in source" << pNow;
1463 }
1464 Import toImport(autoExpPtr->import.uri, v);
1465 newSource = toImport.importedPath();
1466 } else {
1467 newSource = autoExpPtr->import.importedPath();
1468 }
1469 if (newSource && !knownPaths.contains(newSource))
1470 toDo.append(newSource);
1471 } else {
1472 qWarning() << "expected ModuleAutoExport not " << autoExp.internalKindStr()
1473 << "looking up autoExports of" << sourceBase;
1474 Q_ASSERT(false);
1475 }
1476 }
1477 }
1478 if (env)
1479 RefCacheEntry::addForPath(env, selfPath, RefCacheEntry { RefCacheEntry::Cached::All, res });
1480 return res;
1481}
1482
1483bool ImportScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1484{
1485 bool cont = true;
1486 cont = cont && self.dvReferencesField(visitor, Fields::importSources, m_importSourcePaths);
1487 cont = cont && self.dvItemField(visitor, Fields::allSources, [this, &self]() -> DomItem {
1488 return self.subListItem(List::fromQList<Path>(
1489 self.pathFromOwner().withField(Fields::allSources), allSources(self),
1490 [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
1491 return list.subDataItem(p, el.toString());
1492 }));
1493 });
1494 cont = cont && self.dvWrapField(visitor, Fields::qualifiedImports, m_subImports);
1495 cont = cont && self.dvItemField(visitor, Fields::imported, [this, &self]() -> DomItem {
1496 return self.subMapItem(Map(
1497 self.pathFromOwner().withField(Fields::imported),
1498 [this, &self](const DomItem &map, const QString &key) {
1499 return map.subListItem(List::fromQList<DomItem>(
1500 map.pathFromOwner().withKey(key), importedItemsWithName(self, key),
1501 [](const DomItem &, const PathEls::PathComponent &, const DomItem &el) {
1502 return el;
1503 }));
1504 },
1505 [this, &self](const DomItem &) { return this->importedNames(self); },
1506 QLatin1String("List<Export>")));
1507 });
1508 return cont;
1509}
1510
1511bool PropertyInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1512{
1513 bool cont = true;
1514 cont = cont && self.dvValueField(visitor, Fields::propertyDefs, propertyDefs);
1515 cont = cont && self.dvValueField(visitor, Fields::bindings, bindings);
1516 return cont;
1517}
1518
1520
1522{
1523 new (&object) QmlObject(o);
1524}
1525
1528{
1529 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o);
1530}
1531
1532BindingValue::BindingValue(const QList<QmlObject> &l) : kind(BindingValueKind::Array)
1533{
1534 new (&array) QList<QmlObject>(l);
1535}
1536
1538{
1539 clearValue();
1540}
1541
1543{
1544 switch (o.kind) {
1545 case BindingValueKind::Empty:
1546 break;
1547 case BindingValueKind::Object:
1548 new (&object) QmlObject(o.object);
1549 break;
1550 case BindingValueKind::ScriptExpression:
1551 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
1552 break;
1553 case BindingValueKind::Array:
1554 new (&array) QList<QmlObject>(o.array);
1555 }
1556}
1557
1559{
1560 clearValue();
1561 kind = o.kind;
1562 switch (o.kind) {
1563 case BindingValueKind::Empty:
1564 break;
1565 case BindingValueKind::Object:
1566 new (&object) QmlObject(o.object);
1567 break;
1568 case BindingValueKind::ScriptExpression:
1569 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
1570 break;
1571 case BindingValueKind::Array:
1572 new (&array) QList<QmlObject>(o.array);
1573 }
1574 return *this;
1575}
1576
1577DomItem BindingValue::value(const DomItem &binding) const
1578{
1579 switch (kind) {
1580 case BindingValueKind::Empty:
1581 break;
1582 case BindingValueKind::Object:
1583 return binding.copy(&object);
1584 case BindingValueKind::ScriptExpression:
1585 return binding.subOwnerItem(PathEls::Field(Fields::value), scriptExpression);
1586 case BindingValueKind::Array:
1587 return binding.subListItem(List::fromQListRef<QmlObject>(
1588 binding.pathFromOwner().withField(u"value"), array,
1589 [](const DomItem &self, const PathEls::PathComponent &, const QmlObject &obj) {
1590 return self.copy(&obj);
1591 }));
1592 }
1593 return DomItem();
1594}
1595
1597{
1598 switch (kind) {
1599 case BindingValueKind::Empty:
1600 break;
1601 case BindingValueKind::Object:
1602 object.updatePathFromOwner(newPath);
1603 break;
1604 case BindingValueKind::ScriptExpression:
1605 break;
1606 case BindingValueKind::Array:
1607 updatePathFromOwnerQList(array, newPath);
1608 break;
1609 }
1610}
1611
1612void BindingValue::clearValue()
1613{
1614 switch (kind) {
1615 case BindingValueKind::Empty:
1616 break;
1617 case BindingValueKind::Object:
1618 object.~QmlObject();
1619 break;
1620 case BindingValueKind::ScriptExpression:
1621 scriptExpression.~shared_ptr();
1622 break;
1623 case BindingValueKind::Array:
1624 array.~QList<QmlObject>();
1625 break;
1626 }
1627 kind = BindingValueKind::Empty;
1628}
1629
1630ScriptExpression::ScriptExpression(QStringView code, const std::shared_ptr<QQmlJS::Engine> &engine,
1631 AST::Node *ast, const std::shared_ptr<AstComments> &comments,
1632 ExpressionType expressionType, const SourceLocation &localOffset)
1633 : OwningItem(),
1635 m_code(code),
1637 m_ast(ast),
1640{
1641 if (m_expressionType == ExpressionType::BindingExpression)
1642 if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(m_ast))
1643 m_ast = exp->expression;
1644 Q_ASSERT(m_astComments);
1645}
1646
1648{
1649 QMutexLocker l(mutex());
1650 m_expressionType = e.m_expressionType;
1651 m_engine = e.m_engine;
1652 m_ast = e.m_ast;
1653 if (m_codeStr.isEmpty()) {
1654 m_code = e.m_code;
1655 } else {
1656 m_codeStr = e.m_codeStr;
1657 m_code = m_codeStr;
1658 }
1659 m_localOffset = e.m_localOffset;
1660 m_astComments = e.m_astComments;
1661}
1662
1663bool ScriptExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1664{
1665 bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
1666 cont = cont && self.dvValueField(visitor, Fields::code, code());
1667 cont = cont
1668 && self.dvValueLazyField(
1669 visitor, Fields::localOffset,
1670 [this]() { return sourceLocationToQCborValue(localOffset()); },
1671 ConstantData::Options::MapIsMap);
1672 cont = cont && self.dvValueLazyField(visitor, Fields::astRelocatableDump, [this]() {
1673 return astRelocatableDump();
1674 });
1675 cont = cont && self.dvValueField(visitor, Fields::expressionType, int(expressionType()));
1676 if (m_element) {
1677 cont = cont && self.dvItemField(visitor, Fields::scriptElement, [this, &self]() {
1678 return self.subScriptElementWrapperItem(m_element);
1679 });
1680 }
1681 return cont;
1682}
1683
1685{
1686public:
1688 qsizetype maxEnd = std::numeric_limits<qint32>::max(); // see also Lexer::checkFileLength().
1690
1691 FirstNodeVisitor(qsizetype minStart, qsizetype maxEnd) : minStart(minStart), maxEnd(maxEnd) { }
1692
1693 bool preVisit(AST::Node *n) override
1694 {
1695 if (!VisitAll::uiKinds().contains(n->kind)) {
1696 qsizetype start = n->firstSourceLocation().begin();
1697 qsizetype end = n->lastSourceLocation().end();
1698 if (!firstNodeInRange && minStart <= start && end <= maxEnd && start < end)
1699 firstNodeInRange = n;
1700 }
1701 return !firstNodeInRange;
1702 }
1703};
1704
1705AST::Node *firstNodeInRange(AST::Node *n, qsizetype minStart = 0, qsizetype maxEnd = std::numeric_limits<qint32>::max())
1706{
1707 FirstNodeVisitor visitor(minStart, maxEnd);
1708 AST::Node::accept(n, &visitor);
1709 return visitor.firstNodeInRange;
1710}
1711
1712void ScriptExpression::setCode(const QString &code)
1713{
1714 // TODO QTBUG-121933
1715 if (code.isEmpty()) {
1716 return;
1717 }
1718 m_codeStr = code;
1719 m_code = QStringView(m_codeStr);
1720
1721 m_localOffset = SourceLocation();
1722 m_localOffset.length = m_code.size();
1723
1724 m_engine = std::make_shared<QQmlJS::Engine>();
1725 m_astComments = std::make_shared<AstComments>(m_engine);
1726 m_ast = parse();
1727
1728 if (AST::Program *programPtr = AST::cast<AST::Program *>(m_ast)) {
1729 m_ast = programPtr->statements;
1730 }
1731
1732 if (auto *sList = AST::cast<AST::FormalParameterList *>(m_ast)) {
1733 m_ast = sList->element;
1734 }
1735 if (m_expressionType != ExpressionType::FunctionBody) {
1736 if (AST::StatementList *sList = AST::cast<AST::StatementList *>(m_ast)) {
1737 if (!sList->next)
1738 m_ast = sList->statement;
1739 }
1740 }
1741 if (m_expressionType == ExpressionType::BindingExpression)
1742 if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(m_ast))
1743 m_ast = exp->expression;
1744
1745 CommentCollector collector;
1746 collector.collectComments(m_engine, m_ast, m_astComments);
1747}
1748
1749AST::Node *ScriptExpression::parse()
1750{
1751 QQmlJS::Lexer lexer(m_engine.get());
1752 // qmlMode enables QML specific things that plain JS doesn't have, for example type annotations.
1753 const bool qmlMode = m_expressionType != ExpressionType::ESMCode
1754 && m_expressionType != ExpressionType::JSCode;
1755 lexer.setCode(m_codeStr, /*lineno = */ 1, qmlMode);
1756 QQmlJS::Parser parser(m_engine.get());
1757 const bool parserSucceeded = m_expressionType == ExpressionType::ESMCode ? parser.parseModule()
1758 : parser.parseScript();
1759 if (!parserSucceeded) {
1760 addErrorLocal(domParsingErrors().error(tr("Parsing of code failed")));
1761 }
1762 const auto messages = parser.diagnosticMessages();
1763 for (const DiagnosticMessage &msg : messages) {
1764 ErrorMessage err = domParsingErrors().errorMessage(msg);
1765 err.location.offset -= m_localOffset.offset;
1766 err.location.startLine -= m_localOffset.startLine;
1767 if (err.location.startLine == 1)
1768 err.location.startColumn -= m_localOffset.startColumn;
1769 addErrorLocal(std::move(err));
1770 }
1771 return parser.rootNode();
1772}
1773
1774void ScriptExpression::astDumper(const Sink &s, AstDumperOptions options) const
1775{
1776 astNodeDumper(s, ast(), options, 1, 0, [this](SourceLocation astL) {
1777 SourceLocation l = this->locationToLocal(astL);
1778 return this->code().mid(l.offset, l.length);
1779 });
1780}
1781
1783{
1784 return dumperToString([this](const Sink &s) {
1785 this->astDumper(s, AstDumperOption::NoLocations | AstDumperOption::SloppyCompare);
1786 });
1787}
1788
1789void ScriptExpression::writeOut(const DomItem &, OutWriter &lw) const
1790{
1791 reformatAst(lw, this);
1792}
1793
1794QStringView ScriptExpression::loc2Str(const SourceLocation &astL) const
1795{
1796 SourceLocation l = this->locationToLocal(astL); // use engine->code() instead?
1797 return this->code().mid(l.offset, l.length);
1798}
1799
1801{
1802 if (const FileLocations::Tree tree = FileLocations::treeOf(self)) {
1803 return FileLocations::region(tree, MainRegion);
1804 }
1805 return SourceLocation();
1806}
1807
1809{
1810 return typeName.contains(QChar(u'<'));
1811}
1812
1813void PropertyDefinition::writeOut(const DomItem &self, OutWriter &lw) const
1814{
1815 lw.ensureNewline();
1816 const auto fLoc = FileLocations::treeOf(self);
1817 if (isDefaultMember)
1818 lw.writeRegion(fLoc, DefaultKeywordRegion).ensureSpace();
1819 if (isFinal)
1820 lw.writeRegion(fLoc, FinalKeywordRegion).ensureSpace();
1821 if (isRequired)
1822 lw.writeRegion(fLoc, RequiredKeywordRegion).ensureSpace();
1823 if (isReadonly)
1824 lw.writeRegion(fLoc, ReadonlyKeywordRegion).ensureSpace();
1825 if (!typeName.isEmpty()) {
1826 lw.writeRegion(fLoc, PropertyKeywordRegion).ensureSpace();
1827 lw.writeRegion(fLoc, TypeIdentifierRegion, typeName).ensureSpace();
1828 }
1829 lw.writeRegion(fLoc, IdentifierRegion, name);
1830}
1831
1832bool MethodInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1833{
1834 bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor);
1835 cont = cont && self.dvWrapField(visitor, Fields::parameters, parameters);
1836 cont = cont && self.dvValueField(visitor, Fields::methodType, int(methodType));
1837 if (!typeName.isEmpty())
1838 cont = cont && self.dvReferenceField(visitor, Fields::type, typePath(self));
1839 if (methodType == MethodType::Method) {
1840 cont = cont && self.dvValueField(visitor, Fields::isConstructor, isConstructor);
1841 }
1842 if (returnType)
1843 cont = cont && self.dvItemField(visitor, Fields::returnType, [this, &self]() {
1844 return self.subOwnerItem(PathEls::Field(Fields::returnType), returnType);
1845 });
1846 if (body)
1847 cont = cont && self.dvItemField(visitor, Fields::body, [this, &self]() {
1848 return self.subOwnerItem(PathEls::Field(Fields::body), body);
1849 });
1850 return cont;
1851}
1852
1853void MethodInfo::writeOutArguments(const DomItem &self, OutWriter &ow) const
1854{
1855 if (parameters.isEmpty() && methodType == MethodType::Signal)
1856 return;
1857
1858 const auto fLoc = FileLocations::treeOf(self);
1859 ow.writeRegion(fLoc, LeftParenthesisRegion);
1860 bool first = true;
1861 for (const DomItem &arg : self.field(Fields::parameters).values()) {
1862 if (first) {
1863 first = false;
1864 } else {
1865 const auto fLocArg = FileLocations::treeOf(arg);
1866 ow.writeRegion(fLocArg, CommaTokenRegion).ensureSpace();
1867 }
1868 arg.writeOut(ow);
1869 }
1870 ow.writeRegion(fLoc, RightParenthesisRegion);
1871}
1872
1873void MethodInfo::writeOutReturnType(const DomItem &self, OutWriter &ow) const
1874{
1875 if (typeName.isEmpty())
1876 return;
1877
1878 const auto fLoc = FileLocations::treeOf(self);
1879 ow.writeRegion(fLoc, ColonTokenRegion);
1880 ow.ensureSpace();
1881 ow.writeRegion(fLoc, TypeIdentifierRegion, typeName);
1882}
1883
1884void MethodInfo::writeOutBody(const DomItem &self, OutWriter &ow) const
1885{
1886 const auto fLoc = FileLocations::treeOf(self);
1887 ow.ensureSpace().writeRegion(fLoc, LeftBraceRegion);
1888 if (DomItem b = self.field(Fields::body)) {
1889 ow.ensureNewline();
1890 b.writeOut(ow);
1891 }
1892 ow.ensureNewline().writeRegion(fLoc, RightBraceRegion);
1893}
1894
1895void MethodInfo::writeOut(const DomItem &self, OutWriter &ow) const
1896{
1897 const auto fLoc = FileLocations::treeOf(self);
1898 if (methodType == MethodType::Signal) {
1899 ow.writeRegion(fLoc, SignalKeywordRegion).ensureSpace();
1900 } else {
1901 ow.writeRegion(fLoc, FunctionKeywordRegion).ensureSpace();
1902 }
1903 ow.writeRegion(fLoc, IdentifierRegion, name);
1904 writeOutArguments(self, ow);
1905 if (methodType == MethodType::Signal) {
1906 // signal doesn't have returnType or body
1907 return;
1908 }
1909 writeOutReturnType(self, ow);
1910 writeOutBody(self, ow);
1911}
1912
1914{
1915 QString resultStr;
1916 QTextStream res(&resultStr);
1917 LineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"));
1918 OutWriter ow(lw);
1919 ow.indentNextlines = true;
1920 ow.skipComments = true;
1921
1922 writeOutArguments(self, ow);
1923 writeOutReturnType(self, ow);
1924
1925 lw.eof(false);
1926 res.flush();
1927 return resultStr.simplified();
1928}
1929
1930bool MethodParameter::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1931{
1932 bool cont = true;
1933 cont = cont && self.dvValueField(visitor, Fields::name, name);
1934 if (!typeName.isEmpty()) {
1935 cont = cont
1936 && self.dvReferenceField(visitor, Fields::type, Paths::lookupTypePath(typeName));
1937 cont = cont && self.dvValueField(visitor, Fields::typeName, typeName);
1938 }
1939 cont = cont && self.dvValueField(visitor, Fields::isPointer, isPointer);
1940 cont = cont && self.dvValueField(visitor, Fields::isReadonly, isReadonly);
1941 cont = cont && self.dvValueField(visitor, Fields::isList, isList);
1942 cont = cont && self.dvWrapField(visitor, Fields::defaultValue, defaultValue);
1943 cont = cont && self.dvWrapField(visitor, Fields::value, value);
1944
1945 cont = cont && self.dvValueField(visitor, Fields::preCode, u"function f("_s);
1946 cont = cont && self.dvValueField(visitor, Fields::postCode, u") {}"_s);
1947
1948 if (!annotations.isEmpty())
1949 cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
1950 cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
1951 return cont;
1952}
1953
1954void MethodParameter::writeOut(const DomItem &self, OutWriter &ow) const
1955{
1957 writeOutSignal(self, ow);
1958 return;
1959 }
1960
1961 if (!name.isEmpty()) {
1962 const auto fLoc = FileLocations::treeOf(self);
1963 if (isRestElement)
1964 ow.writeRegion(fLoc, EllipsisTokenRegion);
1965 ow.writeRegion(fLoc, IdentifierRegion, name);
1966 if (!typeName.isEmpty())
1967 ow.writeRegion(fLoc, ColonTokenRegion)
1968 .ensureSpace()
1969 .writeRegion(fLoc, TypeIdentifierRegion, typeName);
1970 if (defaultValue) {
1971 ow.ensureSpace().writeRegion(fLoc, EqualTokenRegion).ensureSpace();
1972 self.subOwnerItem(PathEls::Field(Fields::defaultValue), defaultValue).writeOut(ow);
1973 }
1974 } else {
1975 if (value) {
1976 self.subOwnerItem(PathEls::Field(Fields::value), value).writeOut(ow);
1977 }
1978 }
1979}
1980
1981void MethodParameter::writeOutSignal(const DomItem &self, OutWriter &ow) const
1982{
1983 self.writeOutPre(ow);
1984 const auto fLoc = FileLocations::treeOf(self);
1985 if (!typeName.isEmpty())
1986 ow.writeRegion(fLoc, TypeIdentifierRegion, typeName).ensureSpace();
1987 ow.writeRegion(fLoc, IdentifierRegion, name);
1988 self.writeOutPost(ow);
1989}
1990
1991void Pragma::writeOut(const DomItem &self, OutWriter &ow) const
1992{
1993 const auto fLoc = FileLocations::treeOf(self);
1994 ow.ensureNewline();
1995 ow.writeRegion(fLoc, PragmaKeywordRegion)
1996 .ensureSpace()
1997 .writeRegion(fLoc, IdentifierRegion, name);
1998
1999 bool isFirst = true;
2000 for (const auto &value : values) {
2001 if (isFirst) {
2002 isFirst = false;
2003 ow.writeRegion(fLoc, ColonTokenRegion).ensureSpace();
2004 ow.writeRegion(fLoc, PragmaValuesRegion, value);
2005 continue;
2006 }
2007
2008 ow.writeRegion(fLoc, CommaTokenRegion).ensureSpace();
2009 ow.writeRegion(fLoc, PragmaValuesRegion, value);
2010 }
2011 ow.ensureNewline();
2012}
2013
2014bool EnumItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2015{
2016 bool cont = true;
2017 cont = cont && self.dvValueField(visitor, Fields::name, name());
2018 cont = cont && self.dvValueField(visitor, Fields::value, value());
2019 cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
2020 return cont;
2021}
2022
2023void EnumItem::writeOut(const DomItem &self, OutWriter &ow) const
2024{
2025 const auto fLoc = FileLocations::treeOf(self);
2026 index_type myIndex = self.pathFromOwner().last().headIndex();
2027 if (myIndex != 0)
2028 ow.writeRegion(fLoc, CommaTokenRegion);
2029 ow.ensureNewline();
2030 ow.writeRegion(fLoc, IdentifierRegion, name());
2031 if (m_valueKind == ValueKind::ExplicitValue) {
2032 QString v = QString::number(value(), 'f', 0);
2033 if (abs(value() - v.toDouble()) > 1.e-10)
2034 v = QString::number(value());
2035 ow.ensureSpace()
2036 .writeRegion(fLoc, EqualTokenRegion)
2037 .ensureSpace()
2038 .writeRegion(fLoc, EnumValueRegion, v);
2039 }
2040}
2041
2042QmlUri QmlUri::fromString(const QString &str)
2043{
2044 if (str.startsWith(u'"'))
2045 return fromDirectoryString(str.mid(1, str.size() - 2)
2046 .replace(u"\\\""_s, u"\""_s)
2047 .replace(u"\\\\"_s, u"\\"_s));
2048 else
2049 return fromUriString(str);
2050}
2051
2052QmlUri QmlUri::fromUriString(const QString &str)
2053{
2054 QRegularExpression moduleUriRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
2055 return QmlUri((moduleUriRe.match(str).hasMatch() ? Kind::ModuleUri : Kind::Invalid), str);
2056}
2057
2058QmlUri QmlUri::fromDirectoryString(const QString &str)
2059{
2060 QUrl url(str);
2061 if (url.isValid() && url.scheme().size() > 1)
2062 return QmlUri(url);
2063 if (!str.isEmpty()) {
2064 QFileInfo path(str);
2065 return QmlUri((path.isRelative() ? Kind::RelativePath : Kind::AbsolutePath), str);
2066 }
2067 return {};
2068}
2069
2070bool QmlUri::isValid() const
2071{
2072 return m_kind != Kind::Invalid;
2073}
2074
2075bool QmlUri::isDirectory() const
2076{
2077 switch (m_kind) {
2078 case Kind::Invalid:
2079 case Kind::ModuleUri:
2080 break;
2081 case Kind::DirectoryUrl:
2082 case Kind::RelativePath:
2083 case Kind::AbsolutePath:
2084 return true;
2085 }
2086 return false;
2087}
2088
2089bool QmlUri::isModule() const
2090{
2091 return m_kind == Kind::ModuleUri;
2092}
2093
2095{
2096 if (m_kind == Kind::ModuleUri)
2097 return std::get<QString>(m_value);
2098 return QString();
2099}
2100
2102{
2103 switch (m_kind) {
2104 case Kind::Invalid:
2105 case Kind::ModuleUri:
2106 break;
2107 case Kind::DirectoryUrl: {
2108 const QUrl &url = std::get<QUrl>(m_value);
2109 if (url.scheme().compare(u"file", Qt::CaseInsensitive) == 0)
2110 return url.path();
2111 break;
2112 }
2113 case Kind::RelativePath:
2114 case Kind::AbsolutePath:
2115 return std::get<QString>(m_value);
2116 }
2117 return QString();
2118}
2119
2120QString QmlUri::absoluteLocalPath(const QString &basePath) const
2121{
2122 switch (m_kind) {
2123 case Kind::Invalid:
2124 case Kind::ModuleUri:
2125 break;
2126 case Kind::DirectoryUrl: {
2127 const QUrl &url = std::get<QUrl>(m_value);
2128 if (url.scheme().compare(u"file", Qt::CaseInsensitive) == 0)
2129 return url.path();
2130 break;
2131 }
2132 case Kind::RelativePath: {
2133 if (!basePath.isEmpty())
2134 return QDir(basePath).filePath(std::get<QString>(m_value));
2135 break;
2136 }
2137 case Kind::AbsolutePath:
2138 return std::get<QString>(m_value);
2139 }
2140 return QString();
2141}
2142
2144{
2145 if (m_kind == Kind::DirectoryUrl)
2146 return std::get<QUrl>(m_value);
2147 return QUrl {};
2148}
2149
2151{
2152 switch (m_kind) {
2153 case Kind::Invalid:
2154 case Kind::ModuleUri:
2155 break;
2156 case Kind::DirectoryUrl:
2157 return std::get<QUrl>(m_value).toString(); // set formatting? options?
2158 case Kind::RelativePath:
2159 case Kind::AbsolutePath:
2160 return std::get<QString>(m_value);
2161 }
2162 return QString();
2163}
2164
2166{
2167 switch (m_kind) {
2168 case Kind::Invalid:
2169 break;
2170 case Kind::ModuleUri:
2171 return std::get<QString>(m_value);
2172 case Kind::DirectoryUrl:
2173 case Kind::RelativePath:
2174 case Kind::AbsolutePath:
2175 return u"\""_s + directoryString().replace(u'\\', u"\\\\"_s).replace(u'"', u"\\\""_s)
2176 + u"\""_s;
2177 }
2178 return QString();
2179}
2180
2182{
2183 return m_kind;
2184}
2185
2187{
2188 m_element = p;
2189}
2190
2191} // end namespace Dom
2192} // end namespace QQmlJS
2193
2194QT_END_NAMESPACE
2195
2196#include "moc_qqmldomelements_p.cpp"
std::pair< AST::Node *, CommentAnchor > CommentKey
void updatePathFromOwner(const Path &newPath)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr=nullptr)
BindingValue(const QList< QmlObject > &l)
void updatePathFromOwner(const Path &newPath)
BindingValue(const std::shared_ptr< ScriptExpression > &o)
BindingValue(const BindingValue &o)
DomItem value(const DomItem &binding) const
BindingValue & operator=(const BindingValue &o)
BindingValue(const QmlObject &o)
void setValue(std::unique_ptr< BindingValue > &&value)
std::shared_ptr< ScriptExpression > scriptExpressionValue() const
Binding & operator=(const Binding &)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const
Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &a, QmlObject **aPtr=nullptr)
void updatePathFromOwner(const Path &newPath)
QmlObject const * objectValue() const
DomItem valueItem(const DomItem &self) const
QList< QmlObject > * arrayValue()
Binding(const Binding &o)
QList< QmlObject > const * arrayValue() const
void writeOut(const DomItem &self, OutWriter &lw) const
Binding(const QString &m_name, const QString &scriptCode, BindingType bindingType=BindingType::Normal)
Binding(const QString &m_name, std::unique_ptr< BindingValue > &&value, BindingType bindingType=BindingType::Normal)
void writeOutValue(const DomItem &self, OutWriter &lw) const
Binding(const QString &m_name=QString())
Binding(const QString &m_name, const QmlObject &value, BindingType bindingType=BindingType::Normal)
std::shared_ptr< ScriptExpression > scriptExpressionValue()
BindingValueKind valueKind() const
Binding(const QString &m_name, const std::shared_ptr< ScriptExpression > &value, BindingType bindingType=BindingType::Normal)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
CommentableDomElement(const Path &pathFromOwner=Path())
void updatePathFromOwner(const Path &newPath) override
Component(const Path &pathFromOwner=Path())
Path addObject(const QmlObject &object, QmlObject **oPtr=nullptr)
Path attachedTypePath(const DomItem &) const
DomItem field(const DomItem &self, QStringView name) const override
bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override
Component(const QString &name)
virtual void updatePathFromOwner(const Path &newPath)
Path pathFromOwner() const override
A value type that references any element of the Dom.
DomItem top() const
void writeOutPost(OutWriter &lw) const
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
void writeOutPre(OutWriter &lw) const
DomItem environment() const
Path canonicalPath() const
DomItem containingObject() const
DomItem subMapItem(const Map &map) const
DomItem subReferenceItem(const PathEls::PathComponent &c, const Path &referencedObject) const
InternalKind internalKind() const
DomItem owner() const
The owner of an element, for an qmlObject this is the containing qml file.
void writeOut(OutWriter &lw) const
Path pathFromOwner() const
static ErrorGroup domErrorGroup
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
void setAnnotations(const QList< QmlObject > &annotations)
void writeOut(const DomItem &self, OutWriter &lw) const override
void updatePathFromOwner(const Path &newP) override
Path addAnnotation(const QmlObject &child, QmlObject **cPtr=nullptr)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
void writeOut(const DomItem &self, OutWriter &lw) const
Represents a set of tags grouping a set of related error messages.
ErrorMessage warning(const Dumper &message) const
ErrorMessage error(const Dumper &message) const
bool preVisit(AST::Node *n) override
FirstNodeVisitor(qsizetype minStart, qsizetype maxEnd)
Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &ann, QmlObject **aPtr=nullptr)
Id(const QString &idName=QString(), const Path &referredObject=Path())
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const
void updatePathFromOwner(const Path &pathFromOwner)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
QList< Path > allSources(const DomItem &self) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const
void writeOut(const DomItem &self, OutWriter &ow) const
void eof(bool ensureNewline=true)
void writeOut(const DomItem &self, OutWriter &ow, bool compact) const
std::function< index_type(const DomItem &)> Length
std::function< QSet< QString >(const DomItem &)> Keys
Path typePath(const DomItem &) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
void writeOut(const DomItem &self, OutWriter &ow) const
QString signature(const DomItem &self) const
void writeOut(const DomItem &self, OutWriter &ow) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
void writeOutSignal(const DomItem &self, OutWriter &ow) const
TypeAnnotationStyle typeAnnotationStyle
MutableDomItem path(const Path &p)
A DomItem that owns other DomItems and is managed through a shared pointer.
void addErrorLocal(ErrorMessage &&msg)
OwningItem(int derivedFrom=0)
static Path fromRoot(PathRoot r)
Path operator[](int i) const
Path last() const
void writeOut(const DomItem &self, OutWriter &ow) const
void writeOut(const DomItem &self, OutWriter &lw) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
QList< DomItem > subComponents(const DomItem &self) const
void updatePathFromOwner(const Path &newPath) override
void writeOut(const DomItem &self, OutWriter &) const override
QList< QString > subComponentsNames(const DomItem &self) const
MutableDomItem addBinding(MutableDomItem &self, const Binding &binding, AddOption option)
void writeOut(const DomItem &self, OutWriter &ow, const QString &onTarget) const
QList< std::pair< SourceLocation, DomItem > > orderOfAttributes(const DomItem &self, const DomItem &component) const
bool iterateSubOwners(const DomItem &self, function_ref< bool(const DomItem &owner)> visitor) const
void updatePathFromOwner(const Path &newPath) override
Path addBinding(const Binding &binding, AddOption option, Binding **bPtr=nullptr)
static constexpr DomType kindValue
void writeOutSortedEnumerations(const DomItem &component, OutWriter &ow) const
LocallyResolvedAlias resolveAlias(const DomItem &self, std::shared_ptr< ScriptExpression > accessSequence) const
void writeOutId(const DomItem &self, OutWriter &ow) const
LocallyResolvedAlias resolveAlias(const DomItem &self, const QStringList &accessSequence) const
void writeOutSortedPropertyDefinition(const DomItem &self, OutWriter &ow, QSet< QString > &mergedDefBinding) const
Path addPropertyDef(const PropertyDefinition &propertyDef, AddOption option, PropertyDefinition **pDef=nullptr)
MutableDomItem addPropertyDef(MutableDomItem &self, const PropertyDefinition &propertyDef, AddOption option)
DomItem field(const DomItem &self, QStringView name) const override
QString localDefaultPropertyName() const
QString defaultPropertyName(const DomItem &self) const
QmlObject(const Path &pathFromOwner=Path())
void writeOutSortedAttributes(const DomItem &self, OutWriter &ow, const DomItem &component) const
MutableDomItem addMethod(MutableDomItem &self, const MethodInfo &functionDef, AddOption option)
void writeOutAttributes(const DomItem &self, OutWriter &ow, const DomItem &component, const QString &code) const
QList< QString > fields() const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
Path addMethod(const MethodInfo &functionDef, AddOption option, MethodInfo **mPtr=nullptr)
bool iterateBaseDirectSubpaths(const DomItem &self, DirectVisitor) const
QString absoluteLocalPath(const QString &basePath=QString()) const
QString directoryString() const
QString localPath() const
QString moduleUri() const
bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override
Use this to contain any script element.
QStringView loc2Str(const SourceLocation &) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
ScriptExpression(const ScriptExpression &e)
void writeOut(const DomItem &self, OutWriter &lw) const override
SourceLocation globalLocation(const DomItem &self) const
ScriptExpression(QStringView code, const std::shared_ptr< QQmlJS::Engine > &engine, AST::Node *ast, const std::shared_ptr< AstComments > &comments, ExpressionType expressionType, const SourceLocation &localOffset=SourceLocation())
void setScriptElement(const ScriptElementVariant &p)
void astDumper(const Sink &s, AstDumperOptions options) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const
Version(qint32 majorVersion=Undefined, qint32 minorVersion=Undefined)
QString stringValue() const
A vistor that visits all the AST:Node.
Provides entities to maintain mappings between elements and their location in a file.
Tree find(const Tree &self, const Path &p)
Path moduleIndexPath(const QString &uri, int majorVersion, const ErrorHandler &errorHandler=nullptr)
Path moduleScopePath(const QString &uri, const QString &version, const ErrorHandler &errorHandler=nullptr)
Path moduleScopePath(const QString &uri, Version version, const ErrorHandler &errorHandler=nullptr)
static ErrorGroups domParsingErrors()
static QStringList dotExpressionToList(const std::shared_ptr< ScriptExpression > &expr)
std::function< void(const ErrorMessage &)> ErrorHandler
AST::Node * firstNodeInRange(AST::Node *n, qsizetype minStart=0, qsizetype maxEnd=std::numeric_limits< qint32 >::max())
#define NewErrorGroup(name)