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