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

source code of qtdeclarative/src/qmldom/qqmldomelements.cpp