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