Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldomastcreator.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
11#include "qqmldomtop_p.h"
14
15#include <QtQml/private/qqmljsast_p.h>
16#include <QtQmlCompiler/private/qqmljsutils_p.h>
17
18#include <QtCore/QDir>
19#include <QtCore/QFileInfo>
20#include <QtCore/QScopeGuard>
21#include <QtCore/QLoggingCategory>
22
23#include <memory>
24#include <optional>
25#include <type_traits>
26#include <variant>
27#include <vector>
28
29Q_STATIC_LOGGING_CATEGORY(creatorLog, "qt.qmldom.astcreator", QtWarningMsg);
30
31/*
32 Avoid crashing on files with JS-elements that are not implemented yet.
33 Might be removed (definition + usages) once all script elements are implemented.
34*/
35#define Q_SCRIPTELEMENT_DISABLE()
36 do {
37 qCDebug(creatorLog) << "Could not construct the JS DOM at" << __FILE__ << ":" << __LINE__
38 << ", skipping JS elements...";
39 disableScriptElements();
40 } while (false)
41
42#define Q_SCRIPTELEMENT_EXIT_IF(check)
43 do {
44 if (m_enableScriptExpressions && (check)) {
45 Q_SCRIPTELEMENT_DISABLE();
46 return;
47 }
48 } while (false)
49
50QT_BEGIN_NAMESPACE
51namespace QQmlJS {
52namespace Dom {
53
54using namespace AST;
55
56template<typename K, typename V>
57V *valueFromMultimap(QMultiMap<K, V> &mmap, const K &key, index_type idx)
58{
59 if (idx < 0)
60 return nullptr;
61 auto it = mmap.find(key);
62 auto end = mmap.end();
63 if (it == end)
64 return nullptr;
65 auto it2 = it;
66 index_type nEl = 0;
67 while (it2 != end && it2.key() == key) {
68 ++it2;
69 ++nEl;
70 }
71 if (nEl <= idx)
72 return nullptr;
73 for (index_type i = idx + 1; i < nEl; ++i)
74 ++it;
75 return &(*it);
76}
77
78static ErrorGroups astParseErrors()
79{
80 static ErrorGroups errs = { { NewErrorGroup("Dom"), NewErrorGroup("QmlFile"),
81 NewErrorGroup("Parsing") } };
82 return errs;
83}
84
85static QString toString(const UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
86{
87 QString result;
88
89 for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
90 if (iter != qualifiedId)
91 result += delimiter;
92
93 result += iter->name;
94 }
95
96 return result;
97}
98
99static QString typeToString(AST::Type *t)
100{
101 Q_ASSERT(t);
102 QString res = toString(t->typeId);
103
104 if (UiQualifiedId *arg = t->typeArgument)
105 res += u'<' + toString(arg) + u'>';
106
107 return res;
108}
109
110SourceLocation combineLocations(SourceLocation s1, SourceLocation s2)
111{
112 return combine(s1, s2);
113}
114
115SourceLocation combineLocations(Node *n)
116{
117 return combineLocations(n->firstSourceLocation(), n->lastSourceLocation());
118}
119
120static ScriptElementVariant wrapIntoFieldMemberExpression(const ScriptElementVariant &left,
121 const SourceLocation &dotToken,
122 const ScriptElementVariant &right)
123{
124 SourceLocation s1, s2;
125 left.visitConst([&s1](auto &&el) { s1 = el->mainRegionLocation(); });
126 right.visitConst([&s2](auto &&el) { s2 = el->mainRegionLocation(); });
127
128 auto result = std::make_shared<ScriptElements::BinaryExpression>(s1, s2);
129 result->addLocation(OperatorTokenRegion, dotToken);
131 result->setLeft(left);
132 result->setRight(right);
133 return ScriptElementVariant::fromElement(result);
134};
135
136/*!
137 \internal
138 Creates a FieldMemberExpression if the qualified id has dots.
139*/
141fieldMemberExpressionForQualifiedId(const AST::UiQualifiedId *qualifiedId)
142{
143 ScriptElementVariant bindable;
144 bool first = true;
145 for (auto exp = qualifiedId; exp; exp = exp->next) {
146 const SourceLocation identifierLoc = exp->identifierToken;
147 auto id = std::make_shared<ScriptElements::IdentifierExpression>(identifierLoc);
148 id->setName(exp->name);
149 if (first) {
150 first = false;
151 bindable = ScriptElementVariant::fromElement(id);
152 continue;
153 }
154 bindable = wrapIntoFieldMemberExpression(bindable, exp->dotToken,
155 ScriptElementVariant::fromElement(id));
156 }
157
158 return bindable;
159}
160
161QQmlDomAstCreatorBase::QmlStackElement &QQmlDomAstCreatorBase::currentQmlObjectOrComponentEl(int idx)
162{
163 Q_ASSERT_X(idx < nodeStack.size() && idx >= 0, "currentQmlObjectOrComponentEl",
164 "Stack does not contain enough elements!");
165 int i = nodeStack.size() - idx;
166 while (i-- > 0) {
167 DomType k = nodeStack.at(i).item.kind;
168 if (k == DomType::QmlObject || k == DomType::QmlComponent)
169 return nodeStack[i];
170 }
171 Q_ASSERT_X(false, "currentQmlObjectEl", "No QmlObject or component in stack");
172 return nodeStack.last();
173}
174
175QQmlDomAstCreatorBase::QmlStackElement &QQmlDomAstCreatorBase::currentNodeEl(int i)
176{
177 Q_ASSERT_X(i < nodeStack.size() && i >= 0, "currentNode", "Stack does not contain element!");
178 return nodeStack[nodeStack.size() - i - 1];
179}
180
181QQmlDomAstCreatorBase::ScriptStackElement &QQmlDomAstCreatorBase::currentScriptNodeEl(int i)
182{
183 Q_ASSERT_X(i < scriptNodeStack.size() && i >= 0, "currentNode",
184 "Stack does not contain element!");
185 return scriptNodeStack[scriptNodeStack.size() - i - 1];
186}
187
188QQmlDomAstCreatorBase::DomValue &QQmlDomAstCreatorBase::currentNode(int i)
189{
190 Q_ASSERT_X(i < nodeStack.size() && i >= 0, "currentNode",
191 "Stack does not contain element!");
192 return nodeStack[nodeStack.size() - i - 1].item;
193}
194
195void QQmlDomAstCreatorBase::removeCurrentNode(std::optional<DomType> expectedType)
196{
197 Q_ASSERT_X(!nodeStack.isEmpty(), className, "popCurrentNode() without any node");
198 if (expectedType)
199 Q_ASSERT(nodeStack.last().item.kind == *expectedType);
200 nodeStack.removeLast();
201}
202
203void QQmlDomAstCreatorBase::removeCurrentScriptNode(std::optional<DomType> expectedType)
204{
205 Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
206 Q_ASSERT_X(!scriptNodeStack.isEmpty(), className,
207 "popCurrentScriptNode() without any node");
208 if (expectedType)
209 Q_ASSERT(scriptNodeStack.last().kind == *expectedType);
210 scriptNodeStack.removeLast();
211}
212
213/*!
214 \internal
215 Prepares a script element DOM representation such that it can be used inside a QML DOM element.
216 This recursively sets the pathFromOwner and creates the FileLocations::Tree for all children of
217 element.
218
219 Beware that pathFromOwner is appended to ownerFileLocations when creating the FileLocations!
220
221 Make sure to add, for each of its use, a test in tst_qmldomitem:finalizeScriptExpressions, as
222 using a wrong pathFromOwner and/or a wrong base might lead to bugs hard to debug and spurious
223 crashes.
224 */
226QQmlDomAstCreatorBase::finalizeScriptExpression(const ScriptElementVariant &element, const Path &pathFromOwner,
227 const FileLocations::Tree &ownerFileLocations)
228{
229 auto e = element.base();
230 Q_ASSERT(e);
231
232 qCDebug(creatorLog) << "Finalizing script expression with path:"
233 << FileLocations::canonicalPathForTesting(ownerFileLocations)
234 .append(pathFromOwner.toString());
235 e->updatePathFromOwner(pathFromOwner);
236 e->createFileLocations(ownerFileLocations);
237 return element;
238}
239
240FileLocations::Tree QQmlDomAstCreatorBase::createMap(const FileLocations::Tree &base, const Path &p, AST::Node *n)
241{
242 FileLocations::Tree res = FileLocations::ensure(base, p);
243 if (n)
244 FileLocations::addRegion(res, MainRegion, combineLocations(n));
245 return res;
246}
247
248FileLocations::Tree QQmlDomAstCreatorBase::createMap(DomType k, const Path &p, AST::Node *n)
249{
250 Path relative;
251 FileLocations::Tree base;
252 switch (k) {
254 switch (currentNode().kind) {
258 case DomType::Binding:
259 case DomType::Id:
261 break;
262 default:
263 qCWarning(domLog) << "unexpected type" << domTypeToString(currentNode().kind);
264 Q_UNREACHABLE();
265 }
266 base = currentNodeEl().fileLocations;
267 if (p.length() > 2) {
268 Path p2 = p[p.length() - 2];
270 && (p2.checkHeadName(Fields::children) || p2.checkHeadName(Fields::objects)
271 || p2.checkHeadName(Fields::value) || p2.checkHeadName(Fields::annotations)
272 || p2.checkHeadName(Fields::children)))
273 relative = p.mid(p.length() - 2, 2);
274 else if (p.last().checkHeadName(Fields::value)
276 relative = p.last();
277 else {
278 qCWarning(domLog) << "unexpected path to QmlObject in createMap" << p;
279 Q_UNREACHABLE();
280 }
281 } else {
282 qCWarning(domLog) << "unexpected path to QmlObject in createMap" << p;
283 Q_UNREACHABLE();
284 }
285 break;
287 relative = p;
288 base = currentNodeEl().fileLocations;
289 break;
291 case DomType::Pragma:
292 case DomType::Import:
293 case DomType::Id:
295 relative = p;
296 base = rootMap;
297 break;
298 case DomType::Binding:
301 base = currentEl<QmlObject>().fileLocations;
302 if (p.length() > 3)
303 relative = p.mid(p.length() - 3, 3);
304 else
305 relative = p;
306 break;
307
308 default:
309 qCWarning(domLog) << "Unexpected type in createMap:" << domTypeToString(k);
310 Q_UNREACHABLE();
311 break;
312 }
313 return createMap(base, relative, n);
314}
315
317 : qmlFile(qmlFile),
320{
321}
322
323bool QQmlDomAstCreatorBase::visit(UiProgram *program)
324{
325 QFileInfo fInfo(qmlFile.canonicalFilePath());
326 QString componentName = fInfo.baseName();
327 QmlComponent *cPtr;
328 Path p = qmlFilePtr->addComponent(QmlComponent(componentName), AddOption::KeepExisting,
329 &cPtr);
330 MutableDomItem newC(qmlFile.item(), p);
331 Q_ASSERT_X(newC.item(), className, "could not recover component added with addComponent");
332 // QmlFile region == Component region == program span
333 // we hide the component span because the component s written after the imports
334 FileLocations::addRegion(rootMap, MainRegion, combineLocations(program));
335 pushEl(p, *cPtr, program);
336
337 auto envPtr = qmlFile.environment().ownerAs<DomEnvironment>();
338 const bool loadDependencies =
339 !envPtr->options().testFlag(DomEnvironment::Option::NoDependencies);
340 if (m_loadFileLazily && loadDependencies) {
341 envPtr->loadPendingDependencies();
342 envPtr->commitToBase(qmlFile.environment().item());
343 }
344
345 return true;
346}
347
348void QQmlDomAstCreatorBase::endVisit(AST::UiProgram *)
349{
350 MutableDomItem newC = qmlFile.path(currentNodeEl().path);
351 QmlComponent &comp = current<QmlComponent>();
352 for (const Pragma &p : qmlFilePtr->pragmas()) {
353 if (p.name.compare(u"singleton", Qt::CaseInsensitive) == 0) {
354 comp.setIsSingleton(true);
355 comp.setIsCreatable(false); // correct?
356 }
357 }
358 *newC.mutableAs<QmlComponent>() = comp;
359 removeCurrentNode(DomType::QmlComponent);
360 Q_ASSERT_X(nodeStack.isEmpty(), className, "ui program did not finish node stack");
361}
362
363bool QQmlDomAstCreatorBase::visit(UiPragma *el)
364{
365 QStringList valueList;
366 for (auto t = el->values; t; t = t->next)
367 valueList << t->value.toString();
368
369 auto fileLocation = createMap(
370 DomType::Pragma, qmlFilePtr->addPragma(Pragma(el->name.toString(), valueList)), el);
371 FileLocations::addRegion(fileLocation, PragmaKeywordRegion, el->pragmaToken);
372 FileLocations::addRegion(fileLocation, IdentifierRegion, el->pragmaIdToken);
373 if (el->colonToken.isValid()) {
374 FileLocations::addRegion(fileLocation, ColonTokenRegion, el->colonToken);
375 }
376 int i = 0;
377 for (auto t = el->values; t; t = t->next) {
378 auto subMap = createMap(fileLocation, Path().withField(Fields::values).withIndex(i), t);
379 FileLocations::addRegion(subMap, PragmaValuesRegion, t->location);
380 ++i;
381 }
382
383 return true;
384}
385
386bool QQmlDomAstCreatorBase::visit(UiImport *el)
387{
388 Version v(Version::Latest, Version::Latest);
389 if (el->version && el->version->version.hasMajorVersion())
390 v.majorVersion = el->version->version.majorVersion();
391 if (el->version && el->version->version.hasMinorVersion())
392 v.minorVersion = el->version->version.minorVersion();
393
394 auto envPtr = qmlFile.environment().ownerAs<DomEnvironment>();
395 const bool loadDependencies =
396 !envPtr->options().testFlag(DomEnvironment::Option::NoDependencies);
397 FileLocations::Tree fileLocation;
398 if (el->importUri != nullptr) {
399 const Import import =
400 Import::fromUriString(toString(el->importUri), v, el->importId.toString());
401 fileLocation = createMap(DomType::Import, qmlFilePtr->addImport(import), el);
402
403 FileLocations::addRegion(fileLocation, ImportUriRegion, combineLocations(el->importUri));
404 } else {
405 const Import import =
406 Import::fromFileString(el->fileName.toString(), el->importId.toString());
407 fileLocation = createMap(DomType::Import, qmlFilePtr->addImport(import), el);
408
409 FileLocations::addRegion(fileLocation, ImportUriRegion, el->fileNameToken);
410 }
411 if (m_loadFileLazily && loadDependencies) {
412 envPtr->loadPendingDependencies();
413 envPtr->commitToBase(qmlFile.environment().item());
414 }
415
416 if (el->importToken.isValid())
417 FileLocations::addRegion(fileLocation, ImportTokenRegion, el->importToken);
418
419 if (el->asToken.isValid())
420 FileLocations::addRegion(fileLocation, AsTokenRegion, el->asToken);
421
422 if (el->importIdToken.isValid())
423 FileLocations::addRegion(fileLocation, IdNameRegion, el->importIdToken);
424
425 if (el->version)
426 FileLocations::addRegion(fileLocation, VersionRegion, combineLocations(el->version));
427
428
429 return true;
430}
431
432bool QQmlDomAstCreatorBase::visit(AST::UiPublicMember *el)
433{
434 switch (el->type) {
435 case AST::UiPublicMember::Signal: {
436 MethodInfo m;
437 m.name = el->name.toString();
438 m.typeName = toString(el->memberType);
439 m.isReadonly = el->isReadonly();
440 m.access = MethodInfo::Public;
441 m.methodType = MethodInfo::Signal;
442 m.isList = el->typeModifier == QLatin1String("list");
443 MethodInfo *mPtr;
444 Path p = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
445 pushEl(p, *mPtr, el);
446
447 const auto fileLocations = nodeStack.last().fileLocations;
448 FileLocations::addRegion(fileLocations, SignalKeywordRegion, el->propertyToken());
449 FileLocations::addRegion(fileLocations, IdentifierRegion, el->identifierToken);
450 if (el->lparenToken.isValid())
451 FileLocations::addRegion(fileLocations, LeftParenthesisRegion, el->lparenToken);
452 if (el->rparenToken.isValid())
453 FileLocations::addRegion(fileLocations, RightParenthesisRegion, el->rparenToken);
454
455 MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
456 AST::UiParameterList *args = el->parameters;
457 while (args) {
458 MethodParameter param;
459 param.name = args->name.toString();
460 param.typeName = args->type ? args->type->toString() : QString();
461 index_type idx = index_type(mInfo.parameters.size());
462 if (!args->colonToken.isValid())
464 mInfo.parameters.append(param);
465 auto argLocs = FileLocations::ensure(nodeStack.last().fileLocations,
466 Path::fromField(Fields::parameters).withIndex(idx));
467 FileLocations::addRegion(argLocs, MainRegion, combineLocations(args));
468 FileLocations::addRegion(argLocs, IdentifierRegion, args->identifierToken);
469 if (args->type) {
470 if (args->colonToken.isValid())
471 FileLocations::addRegion(argLocs, ColonTokenRegion, args->colonToken);
472 FileLocations::addRegion(argLocs, TypeIdentifierRegion, args->propertyTypeToken);
473 }
474 args = args->next;
475 }
476 break;
477 }
478 case AST::UiPublicMember::Property: {
480 p.name = el->name.toString();
481 p.typeName = toString(el->memberType);
482 p.isReadonly = el->isReadonly();
483 p.isDefaultMember = el->isDefaultMember();
484 p.isRequired = el->isRequired();
485 p.isList = el->typeModifier == QLatin1String("list");
486 p.isVirtual = el->isVirtual();
487 p.isOverride = el->isOverride();
488 p.isFinal = el->isFinal();
489 if (!el->typeModifier.isEmpty())
490 p.typeName = el->typeModifier.toString() + QChar(u'<') + p.typeName + QChar(u'>');
491 PropertyDefinition *pPtr;
492 Path pPathFromOwner =
493 current<QmlObject>().addPropertyDef(p, AddOption::KeepExisting, &pPtr);
494 if (m_enableScriptExpressions) {
495 auto qmlObjectType = makeGenericScriptElement(el->memberType, DomType::ScriptType);
496 qmlObjectType->insertChild(Fields::typeName,
497 fieldMemberExpressionForQualifiedId(el->memberType));
498 pPtr->setNameIdentifiers(finalizeScriptExpression(
499 ScriptElementVariant::fromElement(qmlObjectType),
500 pPathFromOwner.withField(Fields::nameIdentifiers), rootMap));
501 // skip binding identifiers of the binding inside the property definition, if there is
502 // one
503 m_skipBindingIdentifiers = el->binding;
504 }
505 pushEl(pPathFromOwner, *pPtr, el);
506 FileLocations::addRegion(nodeStack.last().fileLocations, PropertyKeywordRegion,
507 el->propertyToken());
508 FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion,
509 el->identifierToken);
510 FileLocations::addRegion(nodeStack.last().fileLocations, TypeIdentifierRegion,
511 el->typeToken);
512 FileLocations::addRegion(nodeStack.last().fileLocations, ColonTokenRegion, el->colonToken);
513 if (el->typeModifierToken.isValid())
514 FileLocations::addRegion(nodeStack.last().fileLocations, TypeModifierRegion, el->typeModifierToken);
515 if (p.name == u"id")
516 qmlFile.addError(std::move(astParseErrors()
517 .warning(tr("id is a special attribute, that should not be "
518 "used as property name"))
519 .withPath(currentNodeEl().path)));
520 if (p.isDefaultMember) {
521 FileLocations::addRegion(nodeStack.last().fileLocations, DefaultKeywordRegion,
522 el->defaultToken());
523 }
524 if (p.isVirtual) {
525 FileLocations::addRegion(nodeStack.last().fileLocations, VirtualKeywordRegion,
526 el->virtualToken());
527 }
528 if (p.isOverride) {
529 FileLocations::addRegion(nodeStack.last().fileLocations, OverrideKeywordRegion,
530 el->overrideToken());
531 }
532 if (p.isFinal) {
533 FileLocations::addRegion(nodeStack.last().fileLocations, FinalKeywordRegion,
534 el->finalToken());
535 }
536 if (p.isRequired) {
537 FileLocations::addRegion(nodeStack.last().fileLocations, RequiredKeywordRegion,
538 el->requiredToken());
539 }
540 if (p.isReadonly) {
541 FileLocations::addRegion(nodeStack.last().fileLocations, ReadonlyKeywordRegion,
542 el->readonlyToken());
543 }
544 if (el->statement) {
545 ++m_nestedFunctionDepth;
547 SourceLocation loc = combineLocations(el->statement);
548 QStringView code = qmlFilePtr->code();
549
550 auto script = std::make_shared<ScriptExpression>(
551 code.mid(loc.offset, loc.length), qmlFilePtr->engine(), el->statement,
552 qmlFilePtr->astComments(), ScriptExpression::ExpressionType::BindingExpression,
553 loc);
554 Binding *bPtr;
555 Path bPathFromOwner = current<QmlObject>().addBinding(Binding(p.name, script, bType),
557 FileLocations::Tree bLoc = createMap(DomType::Binding, bPathFromOwner, el->statement);
558 FileLocations::addRegion(bLoc, ColonTokenRegion, el->colonToken);
559 FileLocations::Tree valueLoc = FileLocations::ensure(bLoc, Path::fromField(Fields::value));
560 FileLocations::addRegion(valueLoc, MainRegion, combineLocations(el->statement));
561 // push it also: its needed in endVisit to add the scriptNode to it
562 // do not use pushEl to avoid recreating the already created "bLoc" Map
563 nodeStack.append({ std::move(bPathFromOwner), *bPtr, std::move(bLoc) });
564 }
565 break;
566 }
567 }
568 return true;
569}
570
571void QQmlDomAstCreatorBase::endVisit(AST::UiPublicMember *el)
572{
573 if (auto &lastEl = currentNode(); lastEl.kind == DomType::Binding) {
574 --m_nestedFunctionDepth;
575 Binding &b = std::get<Binding>(lastEl.value);
576 if (m_enableScriptExpressions
577 && (scriptNodeStack.size() != 1 || scriptNodeStack.last().isList())) {
579 }
580 if (m_enableScriptExpressions) {
581 b.scriptExpressionValue()->setScriptElement(finalizeScriptExpression(
582 currentScriptNodeEl().takeVariant(), Path().withField(Fields::scriptElement),
583 FileLocations::ensure(currentNodeEl().fileLocations,
584 Path().withField(Fields::value))));
585 removeCurrentScriptNode({});
586 }
587
588 QmlObject &containingObject = current<QmlObject>();
589 Binding *bPtr =
590 valueFromMultimap(containingObject.m_bindings, b.name(), currentIndex());
591 Q_ASSERT(bPtr);
592 removeCurrentNode({});
593 }
594 Node::accept(el->parameters, this);
595 loadAnnotations(el);
596 if ((el->binding || el->statement)
597 && nodeStack.last().item.kind == DomType::PropertyDefinition) {
598 PropertyDefinition &pDef = std::get<PropertyDefinition>(nodeStack.last().item.value);
599 if (!pDef.annotations.isEmpty()) {
600 QmlObject duplicate;
601 duplicate.setName(QLatin1String("duplicate"));
602 QmlObject &obj = current<QmlObject>();
603 auto it = obj.m_bindings.find(pDef.name);
604 if (it != obj.m_bindings.end()) {
605 for (QmlObject ann : pDef.annotations) {
606 ann.addAnnotation(duplicate);
607 it->addAnnotation(currentEl<QmlObject>()
608 .path.withField(Fields::bindings)
609 .withKey(pDef.name)
610 .withIndex(obj.m_bindings.values(pDef.name).size() - 1),
611 ann);
612 }
613 }
614 }
615 }
616 QmlObject &obj = current<QmlObject>();
617 QmlStackElement &sEl = nodeStack.last();
618 switch (sEl.item.kind) {
620 PropertyDefinition pDef = std::get<PropertyDefinition>(sEl.item.value);
621 PropertyDefinition *pDefPtr =
622 valueFromMultimap(obj.m_propertyDefs, pDef.name, sEl.path.last().headIndex());
623 Q_ASSERT(pDefPtr);
624 *pDefPtr = std::move(pDef);
625 } break;
626 case DomType::MethodInfo: {
627 MethodInfo m = std::get<MethodInfo>(sEl.item.value);
628 MethodInfo *mPtr = valueFromMultimap(obj.m_methods, m.name, sEl.path.last().headIndex());
629 Q_ASSERT(mPtr);
630 *mPtr = std::move(m);
631 } break;
632 default:
633 Q_UNREACHABLE();
634 }
635 removeCurrentNode({});
636}
637
638void QQmlDomAstCreatorBase::endVisit(AST::FormalParameterList *list)
639{
640 endVisitForLists(list);
641}
642
643bool QQmlDomAstCreatorBase::visit(AST::FunctionExpression *)
644{
645 ++m_nestedFunctionDepth;
646 if (!m_enableScriptExpressions)
647 return false;
648
649 return true;
650}
651
652ScriptElementVariant QQmlDomAstCreatorBase::prepareBodyForFunction(AST::FunctionExpression *fExpression)
653{
654 Q_ASSERT(!scriptNodeStack.isEmpty() || !fExpression->body);
655
656 if (fExpression->body) {
657 if (currentScriptNodeEl().isList()) {
658 // It is more intuitive to have functions with a block as a body instead of a
659 // list.
660 auto body = std::make_shared<ScriptElements::BlockStatement>(
661 combineLocations(fExpression->lbraceToken, fExpression->rbraceToken));
662 body->setStatements(currentScriptNodeEl().takeList());
663 if (auto semanticScope = body->statements().semanticScope())
664 body->setSemanticScope(semanticScope);
665 auto result = ScriptElementVariant::fromElement(body);
666 removeCurrentScriptNode({});
667 return result;
668 } else {
669 auto result = currentScriptNodeEl().takeVariant();
670 removeCurrentScriptNode({});
671 return result;
672 }
673 Q_UNREACHABLE_RETURN({});
674 }
675
676 // for convenience purposes: insert an empty BlockStatement
677 auto body = std::make_shared<ScriptElements::BlockStatement>(
678 combineLocations(fExpression->lbraceToken, fExpression->rbraceToken));
679 return ScriptElementVariant::fromElement(body);
680}
681
682void QQmlDomAstCreatorBase::endVisit(AST::FunctionExpression *fExpression)
683{
684 --m_nestedFunctionDepth;
685 if (!m_enableScriptExpressions)
686 return;
687
688 auto current = makeGenericScriptElement(fExpression, DomType::ScriptFunctionExpression);
689 if (fExpression->identifierToken.isValid())
690 current->addLocation(IdentifierRegion, fExpression->identifierToken);
691 if (fExpression->functionToken.isValid())
692 current->addLocation(FunctionKeywordRegion, fExpression->functionToken);
693 if (fExpression->starToken.isValid())
694 current->addLocation(StarTokenRegion, fExpression->starToken);
695 if (fExpression->lparenToken.isValid())
696 current->addLocation(LeftParenthesisRegion, fExpression->lparenToken);
697 if (fExpression->rparenToken.isValid())
698 current->addLocation(RightParenthesisRegion, fExpression->rparenToken);
699 if (fExpression->lbraceToken.isValid())
700 current->addLocation(LeftBraceRegion, fExpression->lbraceToken);
701 if (fExpression->rbraceToken.isValid())
702 current->addLocation(RightBraceRegion, fExpression->rbraceToken);
703 if (fExpression->typeAnnotation) {
704 current->addLocation(TypeIdentifierRegion,
705 combineLocations(fExpression->typeAnnotation->type));
706 }
707
708 Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() && fExpression->body);
709 current->insertChild(Fields::body, prepareBodyForFunction(fExpression));
710
711 if (fExpression->typeAnnotation) {
713 current->insertChild(Fields::returnType, currentScriptNodeEl().takeVariant());
714 scriptNodeStack.removeLast();
715 }
716 if (fExpression->formals) {
718 current->insertChild(Fields::parameters, currentScriptNodeEl().takeList());
719 scriptNodeStack.removeLast();
720 }
721
722 if (!fExpression->name.isEmpty())
723 current->insertValue(Fields::name, fExpression->name);
724
725 pushScriptElement(current);
726}
727
728bool QQmlDomAstCreatorBase::visit(AST::FunctionDeclaration *fDef)
729{
730 // Treat nested functions as (named) lambdas instead of Qml Object methods.
731 if (m_nestedFunctionDepth > 0) {
732 return visit(static_cast<FunctionExpression *>(fDef));
733 }
734 ++m_nestedFunctionDepth;
735 const QStringView code(qmlFilePtr->code());
736 MethodInfo m;
737 m.name = fDef->name.toString();
738 if (AST::TypeAnnotation *tAnn = fDef->typeAnnotation) {
739 if (AST::Type *t = tAnn->type)
740 m.typeName = typeToString(t);
741 }
742 m.access = MethodInfo::Public;
743 m.methodType = MethodInfo::Method;
744 m.isGenerator = fDef->isGenerator;
745
746 SourceLocation bodyLoc = fDef->body ? combineLocations(fDef->body)
747 : combineLocations(fDef->lbraceToken, fDef->rbraceToken);
748 m.body = std::make_shared<ScriptExpression>(
749 code.mid(bodyLoc.offset, bodyLoc.length), qmlFilePtr->engine(), fDef->body,
750 qmlFilePtr->astComments(), ScriptExpression::ExpressionType::FunctionBody, bodyLoc);
751
752 if (fDef->typeAnnotation) {
753 SourceLocation typeLoc = combineLocations(fDef->typeAnnotation);
754 m.returnType = std::make_shared<ScriptExpression>(
755 code.mid(typeLoc.offset, typeLoc.length), qmlFilePtr->engine(),
756 fDef->typeAnnotation, qmlFilePtr->astComments(),
757 ScriptExpression::ExpressionType::ReturnType, typeLoc);
758 }
759
760 MethodInfo *mPtr;
761 Path mPathFromOwner = current<QmlObject>().addMethod(m, AddOption::KeepExisting, &mPtr);
762 pushEl(mPathFromOwner, *mPtr,
763 fDef); // add at the start and use the normal recursive visit?
764 FileLocations::Tree &fLoc = nodeStack.last().fileLocations;
765 if (fDef->identifierToken.isValid())
766 FileLocations::addRegion(fLoc, IdentifierRegion, fDef->identifierToken);
767 auto bodyTree = FileLocations::ensure(fLoc, Path::fromField(Fields::body));
768 FileLocations::addRegion(bodyTree, MainRegion, bodyLoc);
769 if (fDef->functionToken.isValid())
770 FileLocations::addRegion(fLoc, FunctionKeywordRegion, fDef->functionToken);
771 if (fDef->starToken.isValid())
772 FileLocations::addRegion(fLoc, StarTokenRegion, fDef->starToken);
773 if (fDef->lparenToken.length != 0)
774 FileLocations::addRegion(fLoc, LeftParenthesisRegion, fDef->lparenToken);
775 if (fDef->rparenToken.length != 0)
776 FileLocations::addRegion(fLoc, RightParenthesisRegion, fDef->rparenToken);
777 if (fDef->lbraceToken.length != 0)
778 FileLocations::addRegion(fLoc, LeftBraceRegion, fDef->lbraceToken);
779 if (fDef->rbraceToken.length != 0)
780 FileLocations::addRegion(fLoc, RightBraceRegion, fDef->rbraceToken);
781 if (fDef->typeAnnotation)
782 FileLocations::addRegion(fLoc, TypeIdentifierRegion, combineLocations(fDef->typeAnnotation->type));
783 MethodInfo &mInfo = std::get<MethodInfo>(currentNode().value);
784 AST::FormalParameterList *args = fDef->formals;
785 while (args) {
786 MethodParameter param;
787 param.name = args->element->bindingIdentifier.toString();
788 if (AST::TypeAnnotation *tAnn = args->element->typeAnnotation) {
789 if (AST::Type *t = tAnn->type)
790 param.typeName = typeToString(t);
791 }
792 if (args->element->initializer) {
793 SourceLocation loc = combineLocations(args->element->initializer);
794 param.defaultValue = std::make_shared<ScriptExpression>(
795 code.mid(loc.offset, loc.length), qmlFilePtr->engine(),
796 args->element->initializer, qmlFilePtr->astComments(),
797 ScriptExpression::ExpressionType::ArgInitializer, loc);
798 }
799 if (args->element->type == AST::PatternElement::SpreadElement)
800 param.isRestElement = true;
801 SourceLocation parameterLoc = combineLocations(args->element);
802 param.value = std::make_shared<ScriptExpression>(
803 code.mid(parameterLoc.offset, parameterLoc.length), qmlFilePtr->engine(),
804 args->element, qmlFilePtr->astComments(),
805 ScriptExpression::ExpressionType::ArgumentStructure, parameterLoc);
806
807 index_type idx = index_type(mInfo.parameters.size());
808 mInfo.parameters.append(param);
809 auto argLocs = FileLocations::ensure(nodeStack.last().fileLocations,
810 Path::fromField(Fields::parameters).withIndex(idx));
811 FileLocations::addRegion(argLocs, MainRegion, combineLocations(args));
812 if (args->element->identifierToken.isValid())
813 FileLocations::addRegion(argLocs, IdentifierRegion, args->element->identifierToken);
814 if (args->element->typeAnnotation)
815 FileLocations::addRegion(argLocs, TypeIdentifierRegion, combineLocations(args->element->typeAnnotation->type));
816 args = args->next;
817 }
818 return true;
819}
820
821bool QQmlDomAstCreatorBase::visit(AST::UiSourceElement *el)
822{
823 if (AST::cast<VariableStatement *>(el->sourceElement)) {
824 qmlFile.addError(astParseErrors().warning(
825 "JavaScript declarations are not allowed in QML elements"_L1));
826 return false;
827 }
828 return true;
829}
830
831static void setFormalParameterKind(ScriptElementVariant &variant)
832{
833 if (auto data = variant.data()) {
834 if (auto genericElement =
835 std::get_if<std::shared_ptr<ScriptElements::GenericScriptElement>>(&*data)) {
836 (*genericElement)->setKind(DomType::ScriptFormalParameter);
837 }
838 }
839}
840
841void QQmlDomAstCreatorBase::endVisit(AST::FunctionDeclaration *fDef)
842{
843 // Treat nested functions as (named) lambdas instead of Qml Object methods.
844 if (m_nestedFunctionDepth > 1) {
845 endVisit(static_cast<FunctionExpression *>(fDef));
846 return;
847 }
848 --m_nestedFunctionDepth;
849 MethodInfo &m = std::get<MethodInfo>(currentNode().value);
850 const FileLocations::Tree bodyTree =
851 FileLocations::ensure(currentNodeEl().fileLocations, Path().withField(Fields::body));
852 const Path bodyPath = Path().withField(Fields::scriptElement);
853
854 if (!m_enableScriptExpressions)
855 return;
856
857 Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty() && fDef->body);
858 m.body->setScriptElement(
859 finalizeScriptExpression(prepareBodyForFunction(fDef), bodyPath, bodyTree));
860
861 if (fDef->typeAnnotation) {
862 auto argLoc = FileLocations::ensure(nodeStack.last().fileLocations,
863 Path().withField(Fields::returnType));
864 const Path pathToReturnType = Path().withField(Fields::scriptElement);
865
867 ScriptElementVariant variant = currentScriptNodeEl().takeVariant();
868 finalizeScriptExpression(variant, pathToReturnType, argLoc);
869 m.returnType->setScriptElement(variant);
870 removeCurrentScriptNode({});
871 }
872 if (fDef->formals) {
874 const auto parameterList = scriptNodeStack.takeLast().takeList();
875 const auto &parameterQList = parameterList.qList();
876 size_t size = (size_t)parameterQList.size();
877 for (size_t idx = size - 1; idx < size; --idx) {
878 auto argLoc = FileLocations::ensure(
879 nodeStack.last().fileLocations,
880 Path().withField(Fields::parameters).withIndex(idx).withField(Fields::value));
881 const Path pathToArgument = Path().withField(Fields::scriptElement);
882
883 ScriptElementVariant variant = parameterQList[idx];
884 setFormalParameterKind(variant);
885 finalizeScriptExpression(variant, pathToArgument, argLoc);
886 m.parameters[idx].value->setScriptElement(variant);
887 }
888 }
889
890 // there should be no more uncollected script elements
891 if (m_enableScriptExpressions && !scriptNodeStack.empty()) {
893 }
894}
895
896void QQmlDomAstCreatorBase::endVisit(AST::UiSourceElement *el)
897{
898 if (AST::cast<VariableStatement *>(el->sourceElement))
899 return;
900 MethodInfo &m = std::get<MethodInfo>(currentNode().value);
901 loadAnnotations(el);
902 QmlObject &obj = current<QmlObject>();
903 MethodInfo *mPtr =
904 valueFromMultimap(obj.m_methods, m.name, nodeStack.last().path.last().headIndex());
905 Q_ASSERT(mPtr);
906 *mPtr = m;
907 removeCurrentNode(DomType::MethodInfo);
908}
909
910bool QQmlDomAstCreatorBase::visit(AST::UiObjectDefinition *el)
911{
912 QmlObject scope;
913 scope.setName(toString(el->qualifiedTypeNameId));
914 scope.addPrototypePath(Paths::lookupTypePath(scope.name()));
915 QmlObject *sPtr = nullptr;
916 Path sPathFromOwner;
917 if (!arrayBindingLevels.isEmpty() && nodeStack.size() == arrayBindingLevels.last()) {
918 if (currentNode().kind == DomType::Binding) {
919 QList<QmlObject> *vals = std::get<Binding>(currentNode().value).arrayValue();
920 if (vals) {
921 int idx = vals->size();
922 vals->append(scope);
923 sPathFromOwner = currentNodeEl().path.withField(Fields::value).withIndex(idx);
924 sPtr = &((*vals)[idx]);
925 sPtr->updatePathFromOwner(sPathFromOwner);
926 } else {
927 Q_ASSERT_X(false, className,
928 "expected an array binding with a valid QList<QmlScope> as value");
929 }
930 } else {
931 Q_ASSERT_X(false, className, "expected an array binding as last node on the stack");
932 }
933 } else {
934 DomValue &containingObject = currentQmlObjectOrComponentEl().item;
935 switch (containingObject.kind) {
937 sPathFromOwner = std::get<QmlComponent>(containingObject.value).addObject(scope, &sPtr);
938 break;
940 sPathFromOwner = std::get<QmlObject>(containingObject.value).addChild(scope, &sPtr);
941 break;
942 default:
943 Q_UNREACHABLE();
944 }
945 Path pathFromContainingObject = sPathFromOwner.mid(currentNodeEl().path.length());
946 FileLocations::Tree fLoc =
947 FileLocations::ensure(currentNodeEl().fileLocations, pathFromContainingObject);
948 FileLocations::addRegion(fLoc, IdentifierRegion,
949 el->qualifiedTypeNameId->identifierToken);
950 }
951 Q_ASSERT_X(sPtr, className, "could not recover new scope");
952
953 if (m_enableScriptExpressions) {
954 auto qmlObjectType = makeGenericScriptElement(el->qualifiedTypeNameId, DomType::ScriptType);
955 qmlObjectType->insertChild(Fields::typeName,
956 fieldMemberExpressionForQualifiedId(el->qualifiedTypeNameId));
958 finalizeScriptExpression(ScriptElementVariant::fromElement(qmlObjectType),
959 sPathFromOwner.withField(Fields::nameIdentifiers), rootMap));
960 }
961 pushEl(sPathFromOwner, *sPtr, el);
962
963 if (el->initializer) {
964 FileLocations::addRegion(nodeStack.last().fileLocations, LeftBraceRegion,
965 el->initializer->lbraceToken);
966 FileLocations::addRegion(nodeStack.last().fileLocations, RightBraceRegion,
967 el->initializer->rbraceToken);
968 }
969 loadAnnotations(el);
970 return true;
971}
972
973void QQmlDomAstCreatorBase::endVisit(AST::UiObjectDefinition *)
974{
975 QmlObject &obj = current<QmlObject>();
976 int idx = currentIndex();
977 if (!arrayBindingLevels.isEmpty() && nodeStack.size() == arrayBindingLevels.last() + 1) {
978 if (currentNode(1).kind == DomType::Binding) {
979 Binding &b = std::get<Binding>(currentNode(1).value);
980 QList<QmlObject> *vals = b.arrayValue();
981 Q_ASSERT_X(vals, className,
982 "expected an array binding with a valid QList<QmlScope> as value");
983 (*vals)[idx] = obj;
984 } else {
985 Q_ASSERT_X(false, className, "expected an array binding as last node on the stack");
986 }
987 } else {
988 DomValue &containingObject = currentNodeEl(1).item;
989 Path p = currentNodeEl().path;
990 switch (containingObject.kind) {
992 if (p[p.length() - 2] == Path::fromField(Fields::objects))
993 std::get<QmlComponent>(containingObject.value).m_objects[idx] = obj;
994 else
995 Q_UNREACHABLE();
996 break;
998 if (p[p.length() - 2] == Path::fromField(Fields::children))
999 std::get<QmlObject>(containingObject.value).m_children[idx] = obj;
1000 else
1001 Q_UNREACHABLE();
1002 break;
1003 default:
1004 Q_UNREACHABLE();
1005 }
1006 }
1007 removeCurrentNode(DomType::QmlObject);
1008}
1009
1010void QQmlDomAstCreatorBase::setBindingIdentifiers(const Path &pathFromOwner,
1011 const UiQualifiedId *identifiers, Binding *bindingPtr)
1012{
1013 const bool skipBindingIdentifiers = std::exchange(m_skipBindingIdentifiers, false);
1014 if (!m_enableScriptExpressions || skipBindingIdentifiers)
1015 return;
1016
1017 ScriptElementVariant bindable = fieldMemberExpressionForQualifiedId(identifiers);
1018 bindingPtr->setBindingIdentifiers(finalizeScriptExpression(
1019 bindable, pathFromOwner.withField(Fields::bindingIdentifiers), rootMap));
1020}
1021
1022bool QQmlDomAstCreatorBase::visit(AST::UiObjectBinding *el)
1023{
1024 BindingType bType = (el->hasOnToken ? BindingType::OnBinding : BindingType::Normal);
1025 QmlObject value;
1026 value.setName(toString(el->qualifiedTypeNameId));
1027 Binding *bPtr;
1028 Path bPathFromOwner = current<QmlObject>().addBinding(
1029 Binding(toString(el->qualifiedId), value, bType), AddOption::KeepExisting, &bPtr);
1030 if (bPtr->name() == u"id")
1031 qmlFile.addError(std::move(astParseErrors()
1032 .warning(tr("id attributes should only be a lower case letter "
1033 "followed by letters, numbers or underscore, "
1034 "assuming they refer to an id property"))
1035 .withPath(bPathFromOwner)));
1036 setBindingIdentifiers(bPathFromOwner, el->qualifiedId, bPtr);
1037
1038 pushEl(bPathFromOwner, *bPtr, el);
1039 if (el->hasOnToken)
1040 FileLocations::addRegion(nodeStack.last().fileLocations, OnTokenRegion, el->colonToken);
1041 else
1042 FileLocations::addRegion(nodeStack.last().fileLocations, ColonTokenRegion, el->colonToken);
1043 FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion, combineLocations(el->qualifiedId));
1044 loadAnnotations(el);
1045 QmlObject *objValue = bPtr->objectValue();
1046 Q_ASSERT_X(objValue, className, "could not recover objectValue");
1047 objValue->setName(toString(el->qualifiedTypeNameId));
1048
1049 if (m_enableScriptExpressions) {
1050 auto qmlObjectType = makeGenericScriptElement(el->qualifiedTypeNameId, DomType::ScriptType);
1051 qmlObjectType->insertChild(Fields::typeName,
1052 fieldMemberExpressionForQualifiedId(el->qualifiedTypeNameId));
1053 objValue->setNameIdentifiers(finalizeScriptExpression(
1054 ScriptElementVariant::fromElement(qmlObjectType),
1055 bPathFromOwner.withField(Fields::value).withField(Fields::nameIdentifiers), rootMap));
1056 }
1057
1058 objValue->addPrototypePath(Paths::lookupTypePath(objValue->name()));
1059 pushEl(bPathFromOwner.withField(Fields::value), *objValue, el->initializer);
1060 if (m_enableScriptExpressions && el->initializer) {
1061 FileLocations::addRegion(nodeStack.last().fileLocations, LeftBraceRegion,
1062 el->initializer->lbraceToken);
1063 FileLocations::addRegion(nodeStack.last().fileLocations, RightBraceRegion,
1064 el->initializer->rbraceToken);
1065 }
1066 return true;
1067}
1068
1069void QQmlDomAstCreatorBase::endVisit(AST::UiObjectBinding *)
1070{
1071 QmlObject &objValue = current<QmlObject>();
1072 QmlObject &containingObj = current<QmlObject>(1);
1073 Binding &b = std::get<Binding>(currentNode(1).value);
1074 QmlObject *objPtr = b.objectValue();
1075 Q_ASSERT(objPtr);
1076 *objPtr = objValue;
1077 index_type idx = currentNodeEl(1).path.last().headIndex();
1078 Binding *bPtr = valueFromMultimap(containingObj.m_bindings, b.name(), idx);
1079 Q_ASSERT(bPtr);
1080 *bPtr = b;
1081 removeCurrentNode(DomType::QmlObject);
1082 removeCurrentNode(DomType::Binding);
1083}
1084
1085bool QQmlDomAstCreatorBase::visit(AST::UiScriptBinding *el)
1086{
1087 ++m_nestedFunctionDepth;
1088 QStringView code = qmlFilePtr->code();
1089 SourceLocation loc = combineLocations(el->statement);
1090 const auto script = std::make_shared<ScriptExpression>(
1091 code.mid(loc.offset, loc.length), qmlFilePtr->engine(), el->statement,
1092 qmlFilePtr->astComments(), ScriptExpression::ExpressionType::BindingExpression, loc);
1093 Binding bindingV(toString(el->qualifiedId), script, BindingType::Normal);
1094 Binding *bindingPtr = nullptr;
1095 Id *idPtr = nullptr;
1096 Path pathFromOwner;
1097 if (bindingV.name() == u"id") {
1098 Node *exp = script->ast();
1099 if (ExpressionStatement *eStat = cast<ExpressionStatement *>(script->ast()))
1100 exp = eStat->expression;
1101 if (IdentifierExpression *iExp = cast<IdentifierExpression *>(exp)) {
1102 QmlStackElement &containingObjectEl = currentEl<QmlObject>();
1103 QmlObject &containingObject = std::get<QmlObject>(containingObjectEl.item.value);
1104 QString idName = iExp->name.toString();
1105 Id idVal(idName, qmlFile.canonicalPath().withPath(containingObject.pathFromOwner()));
1106 idVal.value = script;
1107 containingObject.setIdStr(idName);
1108 FileLocations::addRegion(containingObjectEl.fileLocations, IdTokenRegion,
1109 combineLocations(el->qualifiedId));
1110 FileLocations::addRegion(containingObjectEl.fileLocations, IdColonTokenRegion,
1111 el->colonToken);
1112 FileLocations::addRegion(containingObjectEl.fileLocations, IdNameRegion,
1113 iExp->identifierToken);
1114 QmlComponent &comp = current<QmlComponent>();
1115 pathFromOwner = comp.addId(idVal, AddOption::KeepExisting, &idPtr);
1116 QRegularExpression idRe(QRegularExpression::anchoredPattern(
1117 QStringLiteral(uR"([[:lower:]_][[:lower:][:upper:]0-9_]*)")));
1118 auto m = idRe.matchView(iExp->name);
1119 if (!m.hasMatch()) {
1120 const auto m = "id attributes should only be a lower case letter or an underscore "
1121 "followed by letters, numbers or underscores, not %1";
1122 qmlFile.addError(std::move(
1123 astParseErrors().warning(tr(m).arg(iExp->name)).withPath(pathFromOwner)));
1124 }
1125 } else {
1126 pathFromOwner =
1127 current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
1128 Q_ASSERT_X(bindingPtr, className, "binding could not be retrieved");
1129 const auto m = "id attributes should only be a lower case letter or an underscore "
1130 "followed by letters, numbers or underscores, not %1 %2, assuming they "
1131 "refer to a property";
1132 qmlFile.addError(std::move(
1133 astParseErrors()
1134 .warning(tr(m).arg(script->code(), script->astRelocatableDump()))
1135 .withPath(pathFromOwner)));
1136 }
1137 } else {
1138 pathFromOwner =
1139 current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
1140 QmlStackElement &containingObjectEl = currentEl<QmlObject>();
1141 // remove the containingObjectEl.path prefix from pathFromOwner
1142 Path pathFromContainingObject = pathFromOwner.mid(containingObjectEl.path.length());
1143 auto bindingFileLocation =
1144 FileLocations::ensure(containingObjectEl.fileLocations, pathFromContainingObject);
1145 FileLocations::addRegion(bindingFileLocation, IdentifierRegion,
1146 el->qualifiedId->identifierToken);
1147 FileLocations::addRegion(bindingFileLocation, ColonTokenRegion, el->colonToken);
1148
1149 setBindingIdentifiers(pathFromOwner, el->qualifiedId, bindingPtr);
1150
1151 Q_ASSERT_X(bindingPtr, className, "binding could not be retrieved");
1152 }
1153 if (bindingPtr)
1154 pushEl(pathFromOwner, *bindingPtr, el);
1155 else if (idPtr)
1156 pushEl(pathFromOwner, *idPtr, el);
1157 else
1158 Q_UNREACHABLE();
1159 loadAnnotations(el);
1160 // avoid duplicate colon location for id?
1161 FileLocations::addRegion(nodeStack.last().fileLocations, ColonTokenRegion, el->colonToken);
1162 return true;
1163}
1164
1165void QQmlDomAstCreatorBase::setScriptExpression (const std::shared_ptr<ScriptExpression>& value)
1166{
1167 if (m_enableScriptExpressions
1168 && (scriptNodeStack.size() != 1 || currentScriptNodeEl().isList()))
1170 if (m_enableScriptExpressions) {
1171 FileLocations::Tree valueLoc = FileLocations::ensure(currentNodeEl().fileLocations,
1172 Path().withField(Fields::value));
1173 value->setScriptElement(finalizeScriptExpression(currentScriptNodeEl().takeVariant(),
1174 Path().withField(Fields::scriptElement),
1175 valueLoc));
1176 removeCurrentScriptNode({});
1177 }
1178};
1179
1180void QQmlDomAstCreatorBase::endVisit(AST::UiScriptBinding *)
1181{
1182 --m_nestedFunctionDepth;
1183 DomValue &lastEl = currentNode();
1184 index_type idx = currentIndex();
1185 if (lastEl.kind == DomType::Binding) {
1186 Binding &b = std::get<Binding>(lastEl.value);
1187
1188 setScriptExpression(b.scriptExpressionValue());
1189
1190 QmlObject &containingObject = current<QmlObject>();
1191 Binding *bPtr = valueFromMultimap(containingObject.m_bindings, b.name(), idx);
1192 Q_ASSERT(bPtr);
1193 *bPtr = b;
1194 } else if (lastEl.kind == DomType::Id) {
1195 Id &id = std::get<Id>(lastEl.value);
1196
1197 setScriptExpression(id.value);
1198
1199 QmlComponent &comp = current<QmlComponent>();
1200 Id *idPtr = valueFromMultimap(comp.m_ids, id.name, idx);
1201 *idPtr = id;
1202 } else {
1203 Q_UNREACHABLE();
1204 }
1205
1206 // there should be no more uncollected script elements
1207 if (m_enableScriptExpressions && !scriptNodeStack.empty()) {
1209 }
1210 removeCurrentNode({});
1211}
1212
1213bool QQmlDomAstCreatorBase::visit(AST::UiArrayBinding *el)
1214{
1215 QList<QmlObject> value;
1216 Binding bindingV(toString(el->qualifiedId), value, BindingType::Normal);
1217 Binding *bindingPtr;
1218 Path bindingPathFromOwner =
1219 current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
1220 if (bindingV.name() == u"id")
1221 qmlFile.addError(std::move(
1222 astParseErrors()
1223 .error(tr("id attributes should have only simple strings as values"))
1224 .withPath(bindingPathFromOwner)));
1225
1226 setBindingIdentifiers(bindingPathFromOwner, el->qualifiedId, bindingPtr);
1227
1228 pushEl(bindingPathFromOwner, *bindingPtr, el);
1229 FileLocations::addRegion(currentNodeEl().fileLocations, ColonTokenRegion, el->colonToken);
1230 loadAnnotations(el);
1231 FileLocations::Tree arrayList =
1232 createMap(currentNodeEl().fileLocations, Path::fromField(Fields::value), nullptr);
1233 FileLocations::addRegion(arrayList, LeftBracketRegion, el->lbracketToken);
1234 FileLocations::addRegion(arrayList, RightBracketRegion, el->rbracketToken);
1235 arrayBindingLevels.append(nodeStack.size());
1236 return true;
1237}
1238
1239void QQmlDomAstCreatorBase::endVisit(AST::UiArrayBinding *)
1240{
1241 index_type idx = currentIndex();
1242 Binding &b = std::get<Binding>(currentNode().value);
1243 Binding *bPtr = valueFromMultimap(current<QmlObject>().m_bindings, b.name(), idx);
1244 *bPtr = b;
1245 arrayBindingLevels.removeLast();
1246 removeCurrentNode(DomType::Binding);
1247}
1248
1249void QQmlDomAstCreatorBase::endVisit(AST::ArgumentList *list)
1250{
1251 endVisitForLists(list);
1252}
1253
1254bool QQmlDomAstCreatorBase::visit(AST::UiParameterList *)
1255{
1256 return false; // do not create script node for Ui stuff
1257}
1258
1259void QQmlDomAstCreatorBase::endVisit(AST::PatternElementList *list)
1260{
1261 endVisitForLists<AST::PatternElementList>(list, [](AST::PatternElementList *current) {
1262 int toCollect = 0;
1263 toCollect += bool(current->elision);
1264 toCollect += bool(current->element);
1265 return toCollect;
1266 });
1267}
1268
1269void QQmlDomAstCreatorBase::endVisit(AST::PatternPropertyList *list)
1270{
1271 endVisitForLists(list);
1272}
1273
1274/*!
1275 \internal
1276 Implementing the logic of this method in \c QQmlDomAstCreator::visit(AST::UiQualifiedId *)
1277 would create scriptelements at places where there are not needed. This is mainly because
1278 UiQualifiedId's appears inside and outside of script parts.
1279*/
1280ScriptElementVariant QQmlDomAstCreatorBase::scriptElementForQualifiedId(AST::UiQualifiedId *expression)
1281{
1282 auto id = std::make_shared<ScriptElements::IdentifierExpression>(
1283 expression->firstSourceLocation(), expression->lastSourceLocation());
1284 id->setName(expression->toString());
1285
1286 return ScriptElementVariant::fromElement(id);
1287}
1288
1289bool QQmlDomAstCreatorBase::visit(AST::UiQualifiedId *)
1290{
1291 if (!m_enableScriptExpressions)
1292 return false;
1293
1294 return false;
1295}
1296
1297bool QQmlDomAstCreatorBase::visit(AST::UiEnumDeclaration *el)
1298{
1299 EnumDecl eDecl;
1300 eDecl.setName(el->name.toString());
1301 EnumDecl *ePtr;
1302 Path enumPathFromOwner =
1303 current<QmlComponent>().addEnumeration(eDecl, AddOption::KeepExisting, &ePtr);
1304 pushEl(enumPathFromOwner, *ePtr, el);
1305 FileLocations::addRegion(nodeStack.last().fileLocations, EnumKeywordRegion, el->enumToken);
1306 FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion, el->identifierToken);
1307 FileLocations::addRegion(nodeStack.last().fileLocations, LeftBraceRegion, el->lbraceToken);
1308 FileLocations::addRegion(nodeStack.last().fileLocations, RightBraceRegion, el->rbraceToken);
1309 loadAnnotations(el);
1310 return true;
1311}
1312
1313void QQmlDomAstCreatorBase::endVisit(AST::UiEnumDeclaration *)
1314{
1315 EnumDecl &e = std::get<EnumDecl>(currentNode().value);
1316 EnumDecl *ePtr =
1317 valueFromMultimap(current<QmlComponent>().m_enumerations, e.name(), currentIndex());
1318 Q_ASSERT(ePtr);
1319 *ePtr = e;
1320 removeCurrentNode(DomType::EnumDecl);
1321}
1322
1323bool QQmlDomAstCreatorBase::visit(AST::UiEnumMemberList *el)
1324{
1325 EnumItem it(el->member.toString(), el->value,
1326 el->valueToken.isValid() ? EnumItem::ValueKind::ExplicitValue
1328 EnumDecl &eDecl = std::get<EnumDecl>(currentNode().value);
1329 Path itPathFromDecl = eDecl.addValue(it);
1330 const auto map = createMap(DomType::EnumItem, itPathFromDecl, nullptr);
1331 if (el->commaToken.isValid())
1332 FileLocations::addRegion(map, CommaTokenRegion, el->commaToken);
1333 if (el->memberToken.isValid())
1334 FileLocations::addRegion(map, IdentifierRegion, el->memberToken);
1335 if (el->equalToken.isValid())
1336 FileLocations::addRegion(map, EqualTokenRegion, el->equalToken);
1337 if (el->valueToken.isValid())
1338 FileLocations::addRegion(map, EnumValueRegion, el->valueToken);
1340 map, MainRegion, combine(combine(el->memberToken, el->commaToken), el->valueToken));
1341 return true;
1342}
1343
1344void QQmlDomAstCreatorBase::endVisit(AST::UiEnumMemberList *el)
1345{
1346 Node::accept(el->next, this); // put other enum members at the same level as this one...
1347}
1348
1349bool QQmlDomAstCreatorBase::visit(AST::UiInlineComponent *el)
1350{
1351 QStringList els = current<QmlComponent>().name().split(QLatin1Char('.'));
1352 els.append(el->name.toString());
1353 QString cName = els.join(QLatin1Char('.'));
1354 QmlComponent *compPtr;
1355 Path p = qmlFilePtr->addComponent(QmlComponent(cName), AddOption::KeepExisting, &compPtr);
1356
1357 if (m_enableScriptExpressions) {
1358 auto inlineComponentType =
1359 makeGenericScriptElement(el->identifierToken, DomType::ScriptType);
1360
1361 auto typeName = std::make_shared<ScriptElements::IdentifierExpression>(el->identifierToken);
1362 typeName->setName(el->name);
1363 inlineComponentType->insertChild(Fields::typeName,
1364 ScriptElementVariant::fromElement(typeName));
1366 finalizeScriptExpression(ScriptElementVariant::fromElement(inlineComponentType),
1367 p.withField(Fields::nameIdentifiers), rootMap));
1368 }
1369
1370 pushEl(p, *compPtr, el);
1371 FileLocations::addRegion(nodeStack.last().fileLocations, ComponentKeywordRegion,
1372 el->componentToken);
1373 FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion, el->identifierToken);
1374 loadAnnotations(el);
1375 return true;
1376}
1377
1378void QQmlDomAstCreatorBase::endVisit(AST::UiInlineComponent *)
1379{
1380 QmlComponent &component = std::get<QmlComponent>(currentNode().value);
1381 QStringList nameEls = component.name().split(QChar::fromLatin1('.'));
1382 QString key = nameEls.mid(1).join(QChar::fromLatin1('.'));
1383 QmlComponent *cPtr = valueFromMultimap(qmlFilePtr->lazyMembers().m_components, key, currentIndex());
1384 Q_ASSERT(cPtr);
1385 *cPtr = component;
1386 removeCurrentNode(DomType::QmlComponent);
1387}
1388
1389bool QQmlDomAstCreatorBase::visit(UiRequired *el)
1390{
1391 PropertyDefinition pDef;
1392 pDef.name = el->name.toString();
1393 pDef.isRequired = true;
1394 PropertyDefinition *pDefPtr;
1395 Path pathFromOwner =
1396 current<QmlObject>().addPropertyDef(pDef, AddOption::KeepExisting, &pDefPtr);
1397 createMap(DomType::PropertyDefinition, pathFromOwner, el);
1398 return false;
1399}
1400
1401bool QQmlDomAstCreatorBase::visit(AST::UiAnnotation *el)
1402{
1403 QmlObject a;
1404 a.setName(QStringLiteral(u"@") + toString(el->qualifiedTypeNameId));
1405 // add annotation prototype?
1406 DomValue &containingElement = currentNode();
1407 Path pathFromOwner;
1408 QmlObject *aPtr = nullptr;
1409 switch (containingElement.kind) {
1410 case DomType::QmlObject:
1411 pathFromOwner = std::get<QmlObject>(containingElement.value).addAnnotation(a, &aPtr);
1412 break;
1413 case DomType::Binding:
1414 pathFromOwner = std::get<Binding>(containingElement.value)
1415 .addAnnotation(currentNodeEl().path, a, &aPtr);
1416 break;
1417 case DomType::Id:
1418 pathFromOwner =
1419 std::get<Id>(containingElement.value).addAnnotation(currentNodeEl().path, a, &aPtr);
1420 break;
1422 pathFromOwner = std::get<PropertyDefinition>(containingElement.value)
1423 .addAnnotation(currentNodeEl().path, a, &aPtr);
1424 break;
1426 pathFromOwner = std::get<MethodInfo>(containingElement.value)
1427 .addAnnotation(currentNodeEl().path, a, &aPtr);
1428 break;
1429 default:
1430 qCWarning(domLog) << "Unexpected container object for annotation:"
1431 << domTypeToString(containingElement.kind);
1432 Q_UNREACHABLE();
1433 }
1434 pushEl(pathFromOwner, *aPtr, el);
1435 return true;
1436}
1437
1438void QQmlDomAstCreatorBase::endVisit(AST::UiAnnotation *)
1439{
1440 DomValue &containingElement = currentNode(1);
1441 Path pathFromOwner;
1442 QmlObject &a = std::get<QmlObject>(currentNode().value);
1443 switch (containingElement.kind) {
1444 case DomType::QmlObject:
1445 std::get<QmlObject>(containingElement.value).m_annotations[currentIndex()] = a;
1446 break;
1447 case DomType::Binding:
1448 std::get<Binding>(containingElement.value).m_annotations[currentIndex()] = a;
1449 break;
1450 case DomType::Id:
1451 std::get<Id>(containingElement.value).annotations[currentIndex()] = a;
1452 break;
1453 case DomType::PropertyDefinition:
1454 std::get<PropertyDefinition>(containingElement.value).annotations[currentIndex()] = a;
1455 break;
1456 case DomType::MethodInfo:
1457 std::get<MethodInfo>(containingElement.value).annotations[currentIndex()] = a;
1458 break;
1459 default:
1460 Q_UNREACHABLE();
1461 }
1462 removeCurrentNode(DomType::QmlObject);
1463}
1464
1466{
1467 qmlFile.addError(astParseErrors().error(
1468 tr("Maximum statement or expression depth exceeded in QmlDomAstCreator")));
1469}
1470
1471void QQmlDomAstCreatorBase::endVisit(AST::StatementList *list)
1472{
1473 endVisitForLists(list);
1474}
1475
1476bool QQmlDomAstCreatorBase::visit(AST::BinaryExpression *)
1477{
1478 if (!m_enableScriptExpressions)
1479 return false;
1480
1481 return true;
1482}
1483
1484void QQmlDomAstCreatorBase::endVisit(AST::BinaryExpression *exp)
1485{
1486 if (!m_enableScriptExpressions)
1487 return;
1488
1489 auto current = makeScriptElement<ScriptElements::BinaryExpression>(exp);
1490 current->addLocation(OperatorTokenRegion, exp->operatorToken);
1492 current->setRight(currentScriptNodeEl().takeVariant());
1493 removeCurrentScriptNode({});
1495 current->setLeft(currentScriptNodeEl().takeVariant());
1496 removeCurrentScriptNode({});
1497
1498 pushScriptElement(current);
1499}
1500
1501bool QQmlDomAstCreatorBase::visit(AST::Block *)
1502{
1503 if (!m_enableScriptExpressions)
1504 return false;
1505
1506 return true;
1507}
1508
1509void QQmlDomAstCreatorBase::endVisit(AST::Block *block)
1510{
1511 if (!m_enableScriptExpressions)
1512 return;
1513
1514 auto current = makeScriptElement<ScriptElements::BlockStatement>(block);
1515
1516 if (block->statements) {
1518 current->setStatements(currentScriptNodeEl().takeList());
1519 removeCurrentScriptNode(DomType::List);
1520 }
1521
1522 pushScriptElement(current);
1523}
1524
1525bool QQmlDomAstCreatorBase::visit(AST::ForStatement *)
1526{
1527 if (!m_enableScriptExpressions)
1528 return false;
1529
1530 return true;
1531}
1532
1533void QQmlDomAstCreatorBase::endVisit(AST::ForStatement *forStatement)
1534{
1535 if (!m_enableScriptExpressions)
1536 return;
1537
1538 auto current = makeScriptElement<ScriptElements::ForStatement>(forStatement);
1539 current->addLocation(FileLocationRegion::ForKeywordRegion, forStatement->forToken);
1540 current->addLocation(FileLocationRegion::LeftParenthesisRegion, forStatement->lparenToken);
1541 current->addLocation(FileLocationRegion::FirstSemicolonTokenRegion,
1542 forStatement->firstSemicolonToken);
1543 current->addLocation(FileLocationRegion::SecondSemicolonRegion,
1544 forStatement->secondSemicolonToken);
1545 current->addLocation(FileLocationRegion::RightParenthesisRegion, forStatement->rparenToken);
1546
1547 if (forStatement->statement) {
1549 current->setBody(currentScriptNodeEl().takeVariant());
1550 removeCurrentScriptNode(std::nullopt);
1551 }
1552
1553 if (forStatement->expression) {
1555 current->setExpression(currentScriptNodeEl().takeVariant());
1556 removeCurrentScriptNode(std::nullopt);
1557 }
1558
1559 if (forStatement->condition) {
1561 current->setCondition(currentScriptNodeEl().takeVariant());
1562 removeCurrentScriptNode(std::nullopt);
1563 }
1564
1565 if (forStatement->declarations) {
1567 auto variableDeclaration = makeGenericScriptElement(forStatement->declarations,
1569
1570 ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
1573 variableDeclaration->insertChild(Fields::declarations, std::move(list));
1574 removeCurrentScriptNode({});
1575
1576 current->setDeclarations(ScriptElementVariant::fromElement(variableDeclaration));
1577
1578 if (auto pe = forStatement->declarations->declaration;
1579 pe && pe->declarationKindToken.isValid()) {
1580 current->addLocation(FileLocationRegion::TypeIdentifierRegion,
1581 pe->declarationKindToken);
1582 }
1583 }
1584
1585 if (forStatement->initialiser) {
1587 current->setInitializer(currentScriptNodeEl().takeVariant());
1588 removeCurrentScriptNode(std::nullopt);
1589 }
1590 pushScriptElement(current);
1591}
1592
1593bool QQmlDomAstCreatorBase::visit(AST::IdentifierExpression *expression)
1594{
1595 if (!m_enableScriptExpressions)
1596 return false;
1597
1598 auto current = makeScriptElement<ScriptElements::IdentifierExpression>(expression);
1599 current->setName(expression->name);
1600 pushScriptElement(current);
1601 return true;
1602}
1603
1604bool QQmlDomAstCreatorBase::visit(AST::NumericLiteral *expression)
1605{
1606 if (!m_enableScriptExpressions)
1607 return false;
1608
1609 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1610 current->setLiteralValue(expression->value);
1611 pushScriptElement(current);
1612 return true;
1613}
1614
1615bool QQmlDomAstCreatorBase::visit(AST::StringLiteral *expression)
1616{
1617 if (!m_enableScriptExpressions)
1618 return false;
1619
1620 pushScriptElement(makeStringLiteral(expression->value, expression));
1621 return true;
1622}
1623
1624bool QQmlDomAstCreatorBase::visit(AST::NullExpression *expression)
1625{
1626 if (!m_enableScriptExpressions)
1627 return false;
1628
1629 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1630 current->setLiteralValue(nullptr);
1631 pushScriptElement(current);
1632 return true;
1633}
1634
1635bool QQmlDomAstCreatorBase::visit(AST::TrueLiteral *expression)
1636{
1637 if (!m_enableScriptExpressions)
1638 return false;
1639
1640 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1641 current->setLiteralValue(true);
1642 pushScriptElement(current);
1643 return true;
1644}
1645
1646bool QQmlDomAstCreatorBase::visit(AST::FalseLiteral *expression)
1647{
1648 if (!m_enableScriptExpressions)
1649 return false;
1650
1651 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1652 current->setLiteralValue(false);
1653 pushScriptElement(current);
1654 return true;
1655}
1656
1657bool QQmlDomAstCreatorBase::visit(AST::IdentifierPropertyName *expression)
1658{
1659 if (!m_enableScriptExpressions)
1660 return false;
1661
1662 auto current = makeScriptElement<ScriptElements::IdentifierExpression>(expression);
1663 current->setName(expression->id);
1664 pushScriptElement(current);
1665 return true;
1666}
1667
1668bool QQmlDomAstCreatorBase::visit(AST::StringLiteralPropertyName *expression)
1669{
1670 if (!m_enableScriptExpressions)
1671 return false;
1672
1673 pushScriptElement(makeStringLiteral(expression->id, expression));
1674 return true;
1675}
1676
1677bool QQmlDomAstCreatorBase::visit(AST::TypeAnnotation *)
1678{
1679 if (!m_enableScriptExpressions)
1680 return false;
1681
1682 // do nothing: the work is done in (end)visit(AST::Type*).
1683 return true;
1684}
1685
1686bool QQmlDomAstCreatorBase::visit(AST::RegExpLiteral *literal)
1687{
1688 if (!m_enableScriptExpressions)
1689 return false;
1690
1691 auto current = makeGenericScriptElement(literal, DomType::ScriptRegExpLiteral);
1692 current->insertValue(Fields::regExpPattern, literal->pattern);
1693 current->insertValue(Fields::regExpFlags, literal->flags);
1694 pushScriptElement(current);
1695
1696 return true;
1697}
1698
1699bool QQmlDomAstCreatorBase::visit(AST::ThisExpression *expression)
1700{
1701 if (!m_enableScriptExpressions)
1702 return false;
1703
1704 auto current = makeGenericScriptElement(expression, DomType::ScriptThisExpression);
1705 if (expression->thisToken.isValid())
1706 current->addLocation(ThisKeywordRegion, expression->thisToken);
1707 pushScriptElement(current);
1708 return true;
1709}
1710
1711bool QQmlDomAstCreatorBase::visit(AST::SuperLiteral *expression)
1712{
1713 if (!m_enableScriptExpressions)
1714 return false;
1715
1716 auto current = makeGenericScriptElement(expression, DomType::ScriptSuperLiteral);
1717 if (expression->superToken.isValid())
1718 current->addLocation(SuperKeywordRegion, expression->superToken);
1719 pushScriptElement(current);
1720 return true;
1721}
1722
1723bool QQmlDomAstCreatorBase::visit(AST::NumericLiteralPropertyName *expression)
1724{
1725 if (!m_enableScriptExpressions)
1726 return false;
1727
1728 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1729 current->setLiteralValue(expression->id);
1730 pushScriptElement(current);
1731 return true;
1732}
1733
1734bool QQmlDomAstCreatorBase::visit(AST::ComputedPropertyName *)
1735{
1736 if (!m_enableScriptExpressions)
1737 return false;
1738
1739 // nothing to do, just forward the underlying expression without changing/wrapping it
1740 return true;
1741}
1742
1743template<typename T>
1744void QQmlDomAstCreatorBase::endVisitForLists(T *list,
1745 const std::function<int(T *)> &scriptElementsPerEntry)
1746{
1747 if (!m_enableScriptExpressions)
1748 return;
1749
1750 auto current = makeScriptList(list);
1751 for (auto it = list; it; it = it->next) {
1752 const int entriesToCollect = scriptElementsPerEntry ? scriptElementsPerEntry(it) : 1;
1753 for (int i = 0; i < entriesToCollect; ++i) {
1754 Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
1755 auto last = scriptNodeStack.takeLast();
1756 if (last.isList())
1757 current.append(last.takeList());
1758 else
1759 current.append(last.takeVariant());
1760 }
1761 }
1762
1763 current.reverse();
1764 pushScriptElement(current);
1765}
1766
1767void QQmlDomAstCreatorBase::endVisit(AST::VariableDeclarationList *list)
1768{
1769 endVisitForLists(list);
1770}
1771
1772bool QQmlDomAstCreatorBase::visit(AST::Elision *list)
1773{
1774 if (!m_enableScriptExpressions)
1775 return false;
1776
1777 auto currentList = makeScriptList(list);
1778
1779 for (auto it = list; it; it = it->next) {
1780 auto current = makeGenericScriptElement(it->commaToken, DomType::ScriptElision);
1781 currentList.append(ScriptElementVariant::fromElement(current));
1782 }
1783 pushScriptElement(currentList);
1784
1785 return false; // return false because we already iterated over the children using the custom
1786 // iteration above
1787}
1788
1789bool QQmlDomAstCreatorBase::visit(AST::PatternElement *)
1790{
1791 if (!m_enableScriptExpressions)
1792 return false;
1793
1794 return true;
1795}
1796
1797/*!
1798 \internal
1799 Avoid code-duplication, reuse this code when doing endVisit on types inheriting from
1800 AST::PatternElement.
1801*/
1803 AST::PatternElement *pe,
1804 const std::shared_ptr<ScriptElements::GenericScriptElement> &current)
1805{
1806 if (pe->equalToken.isValid())
1807 current->addLocation(FileLocationRegion::EqualTokenRegion, pe->equalToken);
1808
1809 if (pe->identifierToken.isValid() && !pe->bindingIdentifier.isEmpty()) {
1810 auto identifier =
1811 std::make_shared<ScriptElements::IdentifierExpression>(pe->identifierToken);
1812 identifier->setName(pe->bindingIdentifier);
1813 current->insertChild(Fields::identifier, ScriptElementVariant::fromElement(identifier));
1814 }
1815 if (pe->initializer) {
1817 current->insertChild(Fields::initializer, scriptNodeStack.last().takeVariant());
1818 scriptNodeStack.removeLast();
1819 }
1820 if (pe->typeAnnotation) {
1822 current->insertChild(Fields::type, scriptNodeStack.last().takeVariant());
1823 scriptNodeStack.removeLast();
1824 }
1825 if (pe->bindingTarget) {
1827 current->insertChild(Fields::bindingElement, scriptNodeStack.last().takeVariant());
1828 scriptNodeStack.removeLast();
1829 }
1830}
1831
1832void QQmlDomAstCreatorBase::endVisit(AST::PatternElement *pe)
1833{
1834 if (!m_enableScriptExpressions)
1835 return;
1836
1837 auto element = makeGenericScriptElement(pe, DomType::ScriptPattern);
1838 endVisitHelper(pe, element);
1839 // check if helper disabled scriptexpressions
1840 if (!m_enableScriptExpressions)
1841 return;
1842
1843 pushScriptElement(element);
1844}
1845
1846bool QQmlDomAstCreatorBase::visit(AST::IfStatement *)
1847{
1848 if (!m_enableScriptExpressions)
1849 return false;
1850
1851 return true;
1852}
1853
1854void QQmlDomAstCreatorBase::endVisit(AST::IfStatement *ifStatement)
1855{
1856 if (!m_enableScriptExpressions)
1857 return;
1858
1859 auto current = makeScriptElement<ScriptElements::IfStatement>(ifStatement);
1860 current->addLocation(LeftParenthesisRegion, ifStatement->lparenToken);
1861 current->addLocation(RightParenthesisRegion, ifStatement->rparenToken);
1862 current->addLocation(ElseKeywordRegion, ifStatement->elseToken);
1863 current->addLocation(IfKeywordRegion, ifStatement->ifToken);
1864
1865 if (ifStatement->ko) {
1867 current->setAlternative(scriptNodeStack.last().takeVariant());
1868 scriptNodeStack.removeLast();
1869 }
1870
1871 if (ifStatement->ok) {
1873 current->setConsequence(scriptNodeStack.last().takeVariant());
1874 scriptNodeStack.removeLast();
1875 }
1876 if (ifStatement->expression) {
1878 current->setCondition(scriptNodeStack.last().takeVariant());
1879 scriptNodeStack.removeLast();
1880 }
1881
1882 pushScriptElement(current);
1883}
1884
1885bool QQmlDomAstCreatorBase::visit(AST::ReturnStatement *)
1886{
1887 if (!m_enableScriptExpressions)
1888 return false;
1889
1890 return true;
1891}
1892
1893void QQmlDomAstCreatorBase::endVisit(AST::ReturnStatement *returnStatement)
1894{
1895 if (!m_enableScriptExpressions)
1896 return;
1897
1898 auto current = makeScriptElement<ScriptElements::ReturnStatement>(returnStatement);
1899 current->addLocation(ReturnKeywordRegion, returnStatement->returnToken);
1900
1901 if (returnStatement->expression) {
1903 current->setExpression(currentScriptNodeEl().takeVariant());
1904 removeCurrentScriptNode({});
1905 }
1906
1907 pushScriptElement(current);
1908}
1909
1910bool QQmlDomAstCreatorBase::visit(AST::YieldExpression *)
1911{
1912 if (!m_enableScriptExpressions)
1913 return false;
1914
1915 return true;
1916}
1917
1918void QQmlDomAstCreatorBase::endVisit(AST::YieldExpression *yExpression)
1919{
1920 if (!m_enableScriptExpressions)
1921 return;
1922
1923 auto current = makeGenericScriptElement(yExpression, DomType::ScriptYieldExpression);
1924 current->addLocation(YieldKeywordRegion, yExpression->yieldToken);
1925
1926 if (yExpression->expression) {
1928 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
1929 removeCurrentScriptNode({});
1930 }
1931
1932 pushScriptElement(current);
1933}
1934
1935bool QQmlDomAstCreatorBase::visit(AST::FieldMemberExpression *)
1936{
1937 if (!m_enableScriptExpressions)
1938 return false;
1939
1940 return true;
1941}
1942
1943void QQmlDomAstCreatorBase::endVisit(AST::FieldMemberExpression *expression)
1944{
1945 if (!m_enableScriptExpressions)
1946 return;
1947
1948 auto current = makeScriptElement<ScriptElements::BinaryExpression>(expression);
1950 current->addLocation(FileLocationRegion::OperatorTokenRegion, expression->dotToken);
1951
1952 if (expression->base) {
1954 current->setLeft(currentScriptNodeEl().takeVariant());
1955 removeCurrentScriptNode({});
1956 }
1957
1958 auto scriptIdentifier =
1959 std::make_shared<ScriptElements::IdentifierExpression>(expression->identifierToken);
1960 scriptIdentifier->setName(expression->name);
1961 current->setRight(ScriptElementVariant::fromElement(scriptIdentifier));
1962
1963 pushScriptElement(current);
1964}
1965
1966bool QQmlDomAstCreatorBase::visit(AST::ArrayMemberExpression *)
1967{
1968 if (!m_enableScriptExpressions)
1969 return false;
1970
1971 return true;
1972}
1973
1974void QQmlDomAstCreatorBase::endVisit(AST::ArrayMemberExpression *expression)
1975{
1976 if (!m_enableScriptExpressions)
1977 return;
1978
1979 auto current = makeScriptElement<ScriptElements::BinaryExpression>(expression);
1981 current->addLocation(FileLocationRegion::OperatorTokenRegion, expression->lbracketToken);
1982
1983 if (expression->expression) {
1985 // if scriptNodeStack.last() is fieldmember expression, add expression to it instead of
1986 // creating new one
1987 current->setRight(currentScriptNodeEl().takeVariant());
1988 removeCurrentScriptNode({});
1989 }
1990
1991 if (expression->base) {
1993 current->setLeft(currentScriptNodeEl().takeVariant());
1994 removeCurrentScriptNode({});
1995 }
1996
1997 pushScriptElement(current);
1998}
1999
2000bool QQmlDomAstCreatorBase::visit(AST::CallExpression *)
2001{
2002 if (!m_enableScriptExpressions)
2003 return false;
2004
2005 return true;
2006}
2007
2008void QQmlDomAstCreatorBase::endVisit(AST::CallExpression *exp)
2009{
2010 if (!m_enableScriptExpressions)
2011 return;
2012
2013 auto current = makeGenericScriptElement(exp, DomType::ScriptCallExpression);
2014 current->addLocation(LeftParenthesisRegion, exp->lparenToken);
2015 current->addLocation(RightParenthesisRegion, exp->rparenToken);
2016
2017 if (exp->arguments) {
2019 current->insertChild(Fields::arguments, currentScriptNodeEl().takeList());
2020 removeCurrentScriptNode({});
2021 } else {
2022 // insert empty list
2023 current->insertChild(Fields::arguments,
2024 ScriptElements::ScriptList(exp->lparenToken, exp->rparenToken));
2025 }
2026
2027 if (exp->base) {
2029 current->insertChild(Fields::callee, currentScriptNodeEl().takeVariant());
2030 removeCurrentScriptNode({});
2031 }
2032
2033 pushScriptElement(current);
2034}
2035
2036bool QQmlDomAstCreatorBase::visit(AST::ArrayPattern *)
2037{
2038 if (!m_enableScriptExpressions)
2039 return false;
2040
2041 return true;
2042}
2043
2044void QQmlDomAstCreatorBase::endVisit(AST::ArrayPattern *exp)
2045{
2046 if (!m_enableScriptExpressions)
2047 return;
2048
2049 auto current = makeGenericScriptElement(exp, DomType::ScriptArray);
2050
2051 if (exp->elements) {
2053 ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
2055 current->insertChild(Fields::elements, std::move(list));
2056
2057 removeCurrentScriptNode({});
2058 } else {
2059 // insert empty list
2060 current->insertChild(Fields::elements,
2061 ScriptElements::ScriptList(exp->lbracketToken, exp->rbracketToken));
2062 }
2063
2064 pushScriptElement(current);
2065}
2066
2067bool QQmlDomAstCreatorBase::visit(AST::ObjectPattern *)
2068{
2069 if (!m_enableScriptExpressions)
2070 return false;
2071
2072 return true;
2073}
2074
2075void QQmlDomAstCreatorBase::endVisit(AST::ObjectPattern *exp)
2076{
2077 if (!m_enableScriptExpressions)
2078 return;
2079
2080 auto current = makeGenericScriptElement(exp, DomType::ScriptObject);
2081
2082 if (exp->properties) {
2084 current->insertChild(Fields::properties, currentScriptNodeEl().takeList());
2085 removeCurrentScriptNode({});
2086 } else {
2087 // insert empty list
2088 current->insertChild(Fields::properties,
2089 ScriptElements::ScriptList(exp->lbraceToken, exp->rbraceToken));
2090 }
2091
2092 pushScriptElement(current);
2093}
2094
2095bool QQmlDomAstCreatorBase::visit(AST::PatternProperty *)
2096{
2097 if (!m_enableScriptExpressions)
2098 return false;
2099
2100 return true;
2101}
2102
2103void QQmlDomAstCreatorBase::endVisit(AST::PatternProperty *exp)
2104{
2105 if (!m_enableScriptExpressions)
2106 return;
2107
2108 auto current = makeGenericScriptElement(exp, DomType::ScriptProperty);
2109
2110 // handle the stuff from PatternProperty's base class PatternElement
2111 endVisitHelper(static_cast<PatternElement *>(exp), current);
2112
2113 // check if helper disabled scriptexpressions
2114 if (!m_enableScriptExpressions)
2115 return;
2116
2117 if (exp->name) {
2119 current->insertChild(Fields::name, currentScriptNodeEl().takeVariant());
2120 removeCurrentScriptNode({});
2121 }
2122
2123 pushScriptElement(current);
2124}
2125
2126bool QQmlDomAstCreatorBase::visit(AST::VariableStatement *)
2127{
2128 if (!m_enableScriptExpressions)
2129 return false;
2130
2131 return true;
2132}
2133
2134void QQmlDomAstCreatorBase::endVisit(AST::VariableStatement *statement)
2135{
2136 if (!m_enableScriptExpressions)
2137 return;
2138
2139 auto current = makeGenericScriptElement(statement, DomType::ScriptVariableDeclaration);
2140 current->addLocation(FileLocationRegion::TypeIdentifierRegion, statement->declarationKindToken);
2141
2142 if (statement->declarations) {
2144
2145 ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
2148 current->insertChild(Fields::declarations, std::move(list));
2149
2150 removeCurrentScriptNode({});
2151 }
2152
2153 pushScriptElement(current);
2154}
2155
2156bool QQmlDomAstCreatorBase::visit(AST::Type *)
2157{
2158 if (!m_enableScriptExpressions)
2159 return false;
2160
2161 return true;
2162}
2163
2164void QQmlDomAstCreatorBase::endVisit(AST::Type *exp)
2165{
2166 if (!m_enableScriptExpressions)
2167 return;
2168
2169 auto current = makeGenericScriptElement(exp, DomType::ScriptType);
2170
2171 if (exp->typeArgument) {
2172 current->insertChild(Fields::typeArgumentName,
2173 fieldMemberExpressionForQualifiedId(exp->typeArgument));
2174 current->addLocation(FileLocationRegion::IdentifierRegion, combineLocations(exp->typeArgument));
2175 }
2176
2177 if (exp->typeId) {
2178 current->insertChild(Fields::typeName, fieldMemberExpressionForQualifiedId(exp->typeId));
2179 current->addLocation(FileLocationRegion::TypeIdentifierRegion, combineLocations(exp->typeId));
2180 }
2181
2182 pushScriptElement(current);
2183}
2184
2185bool QQmlDomAstCreatorBase::visit(AST::DefaultClause *)
2186{
2187 if (!m_enableScriptExpressions)
2188 return false;
2189
2190 return true;
2191}
2192
2193void QQmlDomAstCreatorBase::endVisit(AST::DefaultClause *exp)
2194{
2195 if (!m_enableScriptExpressions)
2196 return;
2197
2198 auto current = makeGenericScriptElement(exp, DomType::ScriptDefaultClause);
2199 current->addLocation(DefaultKeywordRegion, exp->defaultToken);
2200 current->addLocation(ColonTokenRegion, exp->colonToken);
2201
2202 if (exp->statements) {
2204 current->insertChild(Fields::statements, currentScriptNodeEl().takeList());
2205 removeCurrentScriptNode({});
2206 }
2207
2208 pushScriptElement(current);
2209}
2210
2211bool QQmlDomAstCreatorBase::visit(AST::CaseClause *)
2212{
2213 if (!m_enableScriptExpressions)
2214 return false;
2215
2216 return true;
2217}
2218
2219void QQmlDomAstCreatorBase::endVisit(AST::CaseClause *exp)
2220{
2221 if (!m_enableScriptExpressions)
2222 return;
2223
2224 auto current = makeGenericScriptElement(exp, DomType::ScriptCaseClause);
2225 current->addLocation(FileLocationRegion::CaseKeywordRegion, exp->caseToken);
2226 current->addLocation(FileLocationRegion::ColonTokenRegion, exp->colonToken);
2227
2228 if (exp->statements) {
2230 current->insertChild(Fields::statements, currentScriptNodeEl().takeList());
2231 removeCurrentScriptNode({});
2232 }
2233
2234 if (exp->expression) {
2236 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2237 removeCurrentScriptNode({});
2238 }
2239
2240 pushScriptElement(current);
2241}
2242
2243bool QQmlDomAstCreatorBase::visit(AST::CaseClauses *)
2244{
2245 if (!m_enableScriptExpressions)
2246 return false;
2247
2248 return true;
2249}
2250
2251void QQmlDomAstCreatorBase::endVisit(AST::CaseClauses *list)
2252{
2253 if (!m_enableScriptExpressions)
2254 return;
2255
2256 auto current = makeScriptList(list);
2257
2258 for (auto it = list; it; it = it->next) {
2260 current.append(scriptNodeStack.takeLast().takeVariant());
2261 }
2262
2263 current.reverse();
2264 pushScriptElement(current);
2265}
2266
2267bool QQmlDomAstCreatorBase::visit(AST::CaseBlock *)
2268{
2269 if (!m_enableScriptExpressions)
2270 return false;
2271
2272 return true;
2273}
2274
2275void QQmlDomAstCreatorBase::endVisit(AST::CaseBlock *exp)
2276{
2277 if (!m_enableScriptExpressions)
2278 return;
2279
2280 auto current = makeGenericScriptElement(exp, DomType::ScriptCaseBlock);
2281 current->addLocation(FileLocationRegion::LeftBraceRegion, exp->lbraceToken);
2282 current->addLocation(FileLocationRegion::RightBraceRegion, exp->rbraceToken);
2283
2284 if (exp->moreClauses) {
2286 current->insertChild(Fields::moreCaseClauses, currentScriptNodeEl().takeList());
2287 removeCurrentScriptNode({});
2288 }
2289
2290 if (exp->defaultClause) {
2292 current->insertChild(Fields::defaultClause, currentScriptNodeEl().takeVariant());
2293 removeCurrentScriptNode({});
2294 }
2295
2296 if (exp->clauses) {
2298 current->insertChild(Fields::caseClauses, currentScriptNodeEl().takeList());
2299 removeCurrentScriptNode({});
2300 }
2301 pushScriptElement(current);
2302}
2303
2304bool QQmlDomAstCreatorBase::visit(AST::SwitchStatement *)
2305{
2306 if (!m_enableScriptExpressions)
2307 return false;
2308
2309 return true;
2310}
2311
2312void QQmlDomAstCreatorBase::endVisit(AST::SwitchStatement *exp)
2313{
2314 if (!m_enableScriptExpressions)
2315 return;
2316
2317 auto current = makeGenericScriptElement(exp, DomType::ScriptSwitchStatement);
2318 current->addLocation(FileLocationRegion::SwitchKeywordRegion, exp->switchToken);
2319 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2320 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2321
2322 if (exp->block) {
2324 current->insertChild(Fields::caseBlock, currentScriptNodeEl().takeVariant());
2325 removeCurrentScriptNode({});
2326 }
2327 if (exp->expression) {
2329 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2330 removeCurrentScriptNode({});
2331 }
2332
2333 pushScriptElement(current);
2334}
2335
2336bool QQmlDomAstCreatorBase::visit(AST::WhileStatement *)
2337{
2338 if (!m_enableScriptExpressions)
2339 return false;
2340
2341 return true;
2342}
2343
2344void QQmlDomAstCreatorBase::endVisit(AST::WhileStatement *exp)
2345{
2346 if (!m_enableScriptExpressions)
2347 return;
2348
2349 auto current = makeGenericScriptElement(exp, DomType::ScriptWhileStatement);
2350 current->addLocation(FileLocationRegion::WhileKeywordRegion, exp->whileToken);
2351 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2352 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2353
2354 if (exp->statement) {
2356 current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
2357 removeCurrentScriptNode({});
2358 }
2359
2360 if (exp->expression) {
2362 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2363 removeCurrentScriptNode({});
2364 }
2365
2366 pushScriptElement(current);
2367}
2368
2369bool QQmlDomAstCreatorBase::visit(AST::DoWhileStatement *)
2370{
2371 if (!m_enableScriptExpressions)
2372 return false;
2373
2374 return true;
2375}
2376
2377void QQmlDomAstCreatorBase::endVisit(AST::DoWhileStatement *exp)
2378{
2379 if (!m_enableScriptExpressions)
2380 return;
2381
2382 auto current = makeGenericScriptElement(exp, DomType::ScriptDoWhileStatement);
2383 current->addLocation(FileLocationRegion::DoKeywordRegion, exp->doToken);
2384 current->addLocation(FileLocationRegion::WhileKeywordRegion, exp->whileToken);
2385 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2386 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2387
2388 if (exp->expression) {
2390 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2391 removeCurrentScriptNode({});
2392 }
2393
2394 if (exp->statement) {
2396 current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
2397 removeCurrentScriptNode({});
2398 }
2399
2400 pushScriptElement(current);
2401}
2402
2403bool QQmlDomAstCreatorBase::visit(AST::ForEachStatement *)
2404{
2405 if (!m_enableScriptExpressions)
2406 return false;
2407
2408 return true;
2409}
2410
2411void QQmlDomAstCreatorBase::endVisit(AST::ForEachStatement *exp)
2412{
2413 if (!m_enableScriptExpressions)
2414 return;
2415
2416 auto current = makeGenericScriptElement(exp, DomType::ScriptForEachStatement);
2417 current->addLocation(FileLocationRegion::ForKeywordRegion, exp->forToken);
2418 current->addLocation(FileLocationRegion::InOfTokenRegion, exp->inOfToken);
2419 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2420 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2421
2422 if (exp->statement) {
2424 current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
2425 removeCurrentScriptNode({});
2426 }
2427 if (exp->expression) {
2429 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2430 removeCurrentScriptNode({});
2431 }
2432
2433 if (exp->lhs) {
2435 current->insertChild(Fields::bindingElement, currentScriptNodeEl().takeVariant());
2436 removeCurrentScriptNode({});
2437
2438 if (auto pe = AST::cast<PatternElement *>(exp->lhs);
2439 pe && pe->declarationKindToken.isValid()) {
2440 current->addLocation(FileLocationRegion::TypeIdentifierRegion,
2441 pe->declarationKindToken);
2442 }
2443 }
2444
2445 pushScriptElement(current);
2446}
2447
2448
2449bool QQmlDomAstCreatorBase::visit(AST::ClassExpression *)
2450{
2451 // TODO: Add support for js expressions in classes
2452 // For now, turning off explicitly to avoid unwanted problems
2453 if (m_enableScriptExpressions)
2455 return true;
2456}
2457
2458void QQmlDomAstCreatorBase::endVisit(AST::ClassExpression *)
2459{
2460}
2461
2462void QQmlDomAstCreatorBase::endVisit(AST::TaggedTemplate *literal)
2463{
2464 if (!m_enableScriptExpressions)
2465 return;
2466 auto current = makeGenericScriptElement(literal, DomType::ScriptTaggedTemplate);
2468 current->insertChild(Fields::templateLiteral, scriptNodeStack.takeLast().takeVariant());
2470 current->insertChild(Fields::callee, scriptNodeStack.takeLast().takeVariant());
2471 pushScriptElement(current);
2472}
2473
2474/*!
2475\internal
2476Denotes the position of a template part in a template string. For example, in \c{`a${b}c${d}`}, \c a
2477is \c AtBeginning and \c{${d}} is \c AtEnd while the others are \c InMiddle, and in \c{`a`}, \c a is
2478\c AtBeginning and \c AtEnd.
2479 */
2480enum TemplatePartPosition : quint8 {
2481 InMiddle = 0,
2482 AtBeginning = 0x1,
2483 AtEnd = 0x2,
2484};
2485
2486Q_DECLARE_FLAGS(TemplatePartPositions, TemplatePartPosition)
2487
2488/*!
2489\internal
2490Sets the DollarLeftBraceTokenRegion sourcelocation in currentExpression if templatePartLocation
2491claims that toBeSplit ends in \c{${}.
2492*/
2493static void extractDollarBraceSourceLocationInto(
2494 const std::shared_ptr<ScriptElements::GenericScriptElement> &currentExpression,
2495 const SourceLocation &toBeSplit, QStringView code,
2496 TemplatePartPositions templatePartLocation)
2497{
2498 if (templatePartLocation & AtEnd || !currentExpression)
2499 return;
2500
2501 const auto offset = toBeSplit.offset + toBeSplit.length - 2;
2502 constexpr auto length = quint32(std::char_traits<char>::length("${"));
2503 const auto [row, column] = SourceLocation::rowAndColumnFrom(code, offset, toBeSplit);
2504 currentExpression->addLocation(FileLocationRegion::DollarLeftBraceTokenRegion,
2505 SourceLocation{ offset, length, row, column });
2506 return;
2507}
2508
2509/*!
2510\internal
2511See also \l extractDollarBraceSourceLocationInto.
2512*/
2513static void extractRightBacktickSourceLocationInto(
2514 const std::shared_ptr<ScriptElements::GenericScriptElement> &currentTemplate,
2515 const SourceLocation &toBeSplit, QStringView code,
2516 TemplatePartPositions templatePartLocation)
2517{
2518 if (!(templatePartLocation & AtEnd))
2519 return;
2520
2521 const auto offset = toBeSplit.offset + toBeSplit.length - 1;
2522 constexpr auto length = quint32(std::char_traits<char>::length("`"));
2523 const auto [row, column] = SourceLocation::rowAndColumnFrom(code, offset, toBeSplit);
2524 currentTemplate->addLocation(FileLocationRegion::RightBacktickTokenRegion,
2525 SourceLocation{ offset, length, row, column });
2526}
2527
2528/*!
2529\internal
2530See also \l extractDollarBraceSourceLocationInto.
2531*/
2532static void extractLeftBacktickSourceLocationInto(
2533 const std::shared_ptr<ScriptElements::GenericScriptElement> &currentTemplate,
2534 const SourceLocation &toBeSplit, TemplatePartPositions templatePartLocation)
2535{
2536 if (!(templatePartLocation & AtBeginning))
2537 return;
2538
2539 constexpr auto length = quint32(std::char_traits<char>::length("`"));
2540 const QQmlJS::SourceLocation leftBacktick{ toBeSplit.offset, length, toBeSplit.startLine,
2541 toBeSplit.startColumn };
2542 currentTemplate->addLocation(FileLocationRegion::LeftBacktickTokenRegion, leftBacktick);
2543}
2544
2545/*!
2546\internal
2547See also \l extractDollarBraceSourceLocationInto, but returns the extracted right brace instead of
2548inserting right away.
2549*/
2550static SourceLocation extractRightBraceSourceLocation(const SourceLocation &toBeSplit,
2551 TemplatePartPositions templatePartLocation)
2552{
2553 if (templatePartLocation & AtBeginning)
2554 return SourceLocation{};
2555
2556 // extract } at the beginning and insert in next loop iteration
2557 return SourceLocation{ toBeSplit.offset, 1, toBeSplit.startLine, toBeSplit.startColumn };
2558}
2559
2560/*!
2561\internal
2562Cleans the toBeSplit sourcelocation from potential backticks, dollar braces and right braces to only
2563contain the location of the string part.
2564*/
2565static SourceLocation extractStringLocation(const SourceLocation &toBeSplit, QStringView code,
2566 TemplatePartPositions location)
2567{
2568
2569 // remove "`" or "}" at beginning and "`" or "${" at the end of this location.
2570 const quint32 length = toBeSplit.length - (location & AtEnd ? 2 : 3);
2571 const quint32 offset = toBeSplit.offset + 1;
2572 const auto [row, column] = SourceLocation::rowAndColumnFrom(code, offset, toBeSplit);
2573 return SourceLocation{ offset, length, row, column };
2574}
2575
2576void QQmlDomAstCreatorBase::endVisit(AST::TemplateLiteral *literal)
2577{
2578 if (!m_enableScriptExpressions)
2579 return;
2580
2581 // AST::TemplateLiteral is a list and a TemplateLiteral at the same time:
2582 // in the Dom representation wrap the list into a separate TemplateLiteral Item.
2583 auto currentList = makeScriptList(literal);
2584 auto currentTemplate = makeGenericScriptElement(literal, DomType::ScriptTemplateLiteral);
2585
2586 const auto children = [&literal]() {
2587 std::vector<AST::TemplateLiteral *> result;
2588 for (auto it = literal; it; it = it->next) {
2589 result.push_back(it);
2590 }
2591 return result;
2592 }();
2593
2594 SourceLocation rightBrace;
2595 for (auto it = children.crbegin(); it != children.crend(); ++it) {
2596 // literalToken contains "`", "${", "}", for example "`asdf${" or "}asdf${"
2597 const QQmlJS::SourceLocation toBeSplit = (*it)->literalToken;
2598 std::shared_ptr<ScriptElements::GenericScriptElement> currentExpression;
2599
2600 if ((*it)->expression) {
2602 currentExpression = makeGenericScriptElement((*it)->expression,
2604
2605 currentExpression->insertChild(Fields::expression,
2606 scriptNodeStack.takeLast().takeVariant());
2607 if (rightBrace.isValid()) {
2608 currentExpression->addLocation(FileLocationRegion::RightBraceRegion,
2609 std::exchange(rightBrace, SourceLocation{}));
2610 }
2611 currentList.append(ScriptElementVariant::fromElement(currentExpression));
2612 }
2613
2614 if (!toBeSplit.isValid())
2615 continue;
2616
2617 const TemplatePartPositions location = [&it, &children]() {
2618 TemplatePartPositions result;
2619 if (it == children.crbegin())
2620 result |= AtEnd;
2621 if (it == std::prev(children.crend()))
2622 result |= AtBeginning;
2623 return result;
2624 }();
2625
2626 extractRightBacktickSourceLocationInto(currentTemplate, toBeSplit, qmlFilePtr->code(),
2627 location);
2628 extractLeftBacktickSourceLocationInto(currentTemplate, toBeSplit, location);
2629
2630 extractDollarBraceSourceLocationInto(currentExpression, toBeSplit, qmlFilePtr->code(),
2631 location);
2632 rightBrace = extractRightBraceSourceLocation(toBeSplit, location);
2633
2634 if ((*it)->rawValue.isEmpty())
2635 continue;
2636
2637 const SourceLocation stringLocation =
2638 extractStringLocation(toBeSplit, qmlFilePtr->code(), location);
2639 auto currentString =
2640 makeGenericScriptElement(stringLocation, DomType::ScriptTemplateStringPart);
2641 currentString->insertValue(Fields::value, (*it)->rawValue);
2642
2643 currentList.append(ScriptElementVariant::fromElement(currentString));
2644 }
2645 currentList.reverse();
2646
2647 currentTemplate->insertChild(Fields::components, currentList);
2648 pushScriptElement(currentTemplate);
2649}
2650
2651bool QQmlDomAstCreatorBase::visit(AST::TryStatement *)
2652{
2653 return m_enableScriptExpressions;
2654}
2655
2656void QQmlDomAstCreatorBase::endVisit(AST::TryStatement *statement)
2657{
2658 if (!m_enableScriptExpressions)
2659 return;
2660
2661 auto current = makeGenericScriptElement(statement, DomType::ScriptTryCatchStatement);
2662 current->addLocation(FileLocationRegion::TryKeywordRegion, statement->tryToken);
2663
2664 if (auto exp = statement->finallyExpression) {
2665 current->addLocation(FileLocationRegion::FinallyKeywordRegion, exp->finallyToken);
2666
2668 current->insertChild(Fields::finallyBlock, currentScriptNodeEl().takeVariant());
2669 removeCurrentScriptNode({});
2670 }
2671
2672 if (auto exp = statement->catchExpression) {
2673 current->addLocation(FileLocationRegion::CatchKeywordRegion, exp->catchToken);
2674 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2675 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2676
2678 current->insertChild(Fields::catchBlock, currentScriptNodeEl().takeVariant());
2679 removeCurrentScriptNode({});
2681 current->insertChild(Fields::catchParameter, currentScriptNodeEl().takeVariant());
2682 removeCurrentScriptNode({});
2683 }
2684
2685 if (statement->statement) {
2687 current->insertChild(Fields::block, currentScriptNodeEl().takeVariant());
2688 removeCurrentScriptNode({});
2689 }
2690
2691 pushScriptElement(current);
2692}
2693
2694bool QQmlDomAstCreatorBase::visit(AST::Catch *)
2695{
2696 // handled in visit(AST::TryStatement* )
2697 return m_enableScriptExpressions;
2698}
2699
2700void QQmlDomAstCreatorBase::endVisit(AST::Catch *)
2701{
2702 // handled in endVisit(AST::TryStatement* )
2703}
2704
2705bool QQmlDomAstCreatorBase::visit(AST::Finally *)
2706{
2707 // handled in visit(AST::TryStatement* )
2708 return m_enableScriptExpressions;
2709}
2710
2711void QQmlDomAstCreatorBase::endVisit(AST::Finally *)
2712{
2713 // handled in endVisit(AST::TryStatement* )
2714}
2715
2716bool QQmlDomAstCreatorBase::visit(AST::ThrowStatement *)
2717{
2718 return m_enableScriptExpressions;
2719}
2720
2721void QQmlDomAstCreatorBase::endVisit(AST::ThrowStatement *statement)
2722{
2723 if (!m_enableScriptExpressions)
2724 return;
2725
2726 auto current = makeGenericScriptElement(statement, DomType::ScriptThrowStatement);
2727 current->addLocation(FileLocationRegion::ThrowKeywordRegion, statement->throwToken);
2728
2729 if (statement->expression) {
2731 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2732 removeCurrentScriptNode({});
2733 }
2734
2735 pushScriptElement(current);
2736}
2737
2738bool QQmlDomAstCreatorBase::visit(AST::LabelledStatement *)
2739{
2740 return m_enableScriptExpressions;
2741}
2742
2743void QQmlDomAstCreatorBase::endVisit(AST::LabelledStatement *statement)
2744{
2745 if (!m_enableScriptExpressions)
2746 return;
2747
2748 auto current = makeGenericScriptElement(statement, DomType::ScriptLabelledStatement);
2749 current->addLocation(FileLocationRegion::ColonTokenRegion, statement->colonToken);
2750
2751 auto label = std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
2752 label->setName(statement->label);
2753 current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
2754
2755
2756 if (statement->statement) {
2758 current->insertChild(Fields::statement, currentScriptNodeEl().takeVariant());
2759 removeCurrentScriptNode({});
2760 }
2761
2762 pushScriptElement(current);
2763}
2764
2765bool QQmlDomAstCreatorBase::visit(AST::BreakStatement *)
2766{
2767 return m_enableScriptExpressions;
2768}
2769
2770void QQmlDomAstCreatorBase::endVisit(AST::BreakStatement *statement)
2771{
2772 if (!m_enableScriptExpressions)
2773 return;
2774
2775 auto current = makeGenericScriptElement(statement, DomType::ScriptBreakStatement);
2776 current->addLocation(FileLocationRegion::BreakKeywordRegion, statement->breakToken);
2777
2778 if (!statement->label.isEmpty()) {
2779 auto label =
2780 std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
2781 label->setName(statement->label);
2782 current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
2783 }
2784
2785 pushScriptElement(current);
2786}
2787
2788bool QQmlDomAstCreatorBase::visit(AST::CommaExpression *)
2789{
2790 return m_enableScriptExpressions;
2791}
2792
2793void QQmlDomAstCreatorBase::endVisit(AST::CommaExpression *commaExpression)
2794{
2795 if (!m_enableScriptExpressions)
2796 return;
2797
2798 auto current = makeScriptElement<ScriptElements::BinaryExpression>(commaExpression);
2799 current->addLocation(OperatorTokenRegion, commaExpression->commaToken);
2800
2801 if (commaExpression->right) {
2803 current->setRight(currentScriptNodeEl().takeVariant());
2804 removeCurrentScriptNode({});
2805 }
2806
2807 if (commaExpression->left) {
2809 current->setLeft(currentScriptNodeEl().takeVariant());
2810 removeCurrentScriptNode({});
2811 }
2812
2813 pushScriptElement(current);
2814}
2815
2816bool QQmlDomAstCreatorBase::visit(AST::ConditionalExpression *)
2817{
2818 return m_enableScriptExpressions;
2819}
2820
2821void QQmlDomAstCreatorBase::endVisit(AST::ConditionalExpression *expression)
2822{
2823 if (!m_enableScriptExpressions)
2824 return;
2825
2826 auto current = makeGenericScriptElement(expression, DomType::ScriptConditionalExpression);
2827 current->addLocation(FileLocationRegion::QuestionMarkTokenRegion, expression->questionToken);
2828 current->addLocation(FileLocationRegion::ColonTokenRegion, expression->colonToken);
2829
2830 if (expression->ko) {
2832 current->insertChild(Fields::alternative, currentScriptNodeEl().takeVariant());
2833 removeCurrentScriptNode({});
2834 }
2835
2836 if (expression->ok) {
2838 current->insertChild(Fields::consequence, currentScriptNodeEl().takeVariant());
2839 removeCurrentScriptNode({});
2840 }
2841
2842 if (expression->expression) {
2844 current->insertChild(Fields::condition, currentScriptNodeEl().takeVariant());
2845 removeCurrentScriptNode({});
2846 }
2847
2848 pushScriptElement(current);
2849}
2850
2851bool QQmlDomAstCreatorBase::visit(AST::ContinueStatement *)
2852{
2853 return m_enableScriptExpressions;
2854}
2855
2856void QQmlDomAstCreatorBase::endVisit(AST::ContinueStatement *statement)
2857{
2858 if (!m_enableScriptExpressions)
2859 return;
2860
2861 auto current = makeGenericScriptElement(statement, DomType::ScriptContinueStatement);
2862 current->addLocation(FileLocationRegion::ContinueKeywordRegion, statement->continueToken);
2863
2864 if (!statement->label.isEmpty()) {
2865 auto label =
2866 std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
2867 label->setName(statement->label);
2868 current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
2869 }
2870
2871 pushScriptElement(current);
2872}
2873
2874/*!
2875 \internal
2876 Helper to create unary expressions from AST nodes.
2877 \sa makeGenericScriptElement
2878 */
2880QQmlDomAstCreatorBase::makeUnaryExpression(AST::Node *expression, QQmlJS::SourceLocation operatorToken,
2881 bool hasExpression, UnaryExpressionKind kind)
2882{
2883 const DomType type = [&kind]() {
2884 switch (kind) {
2885 case Prefix:
2887 case Postfix:
2889 }
2890 Q_UNREACHABLE_RETURN(DomType::ScriptUnaryExpression);
2891 }();
2892
2893 auto current = makeGenericScriptElement(expression, type);
2894 current->addLocation(FileLocationRegion::OperatorTokenRegion, operatorToken);
2895
2896 if (hasExpression) {
2899 return {};
2900 }
2901 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2902 removeCurrentScriptNode({});
2903 }
2904
2905 return current;
2906}
2907
2908bool QQmlDomAstCreatorBase::visit(AST::UnaryMinusExpression *)
2909{
2910 return m_enableScriptExpressions;
2911}
2912
2913void QQmlDomAstCreatorBase::endVisit(AST::UnaryMinusExpression *statement)
2914{
2915 if (!m_enableScriptExpressions)
2916 return;
2917
2918 auto current =
2919 makeUnaryExpression(statement, statement->minusToken, statement->expression, Prefix);
2920 if (!current)
2921 return;
2922
2923 pushScriptElement(current);
2924}
2925
2926bool QQmlDomAstCreatorBase::visit(AST::UnaryPlusExpression *)
2927{
2928 return m_enableScriptExpressions;
2929}
2930
2931void QQmlDomAstCreatorBase::endVisit(AST::UnaryPlusExpression *statement)
2932{
2933 if (!m_enableScriptExpressions)
2934 return;
2935
2936 auto current =
2937 makeUnaryExpression(statement, statement->plusToken, statement->expression, Prefix);
2938 if (!current)
2939 return;
2940
2941 pushScriptElement(current);
2942}
2943
2944bool QQmlDomAstCreatorBase::visit(AST::TildeExpression *)
2945{
2946 return m_enableScriptExpressions;
2947}
2948
2949void QQmlDomAstCreatorBase::endVisit(AST::TildeExpression *statement)
2950{
2951 if (!m_enableScriptExpressions)
2952 return;
2953
2954 auto current =
2955 makeUnaryExpression(statement, statement->tildeToken, statement->expression, Prefix);
2956 if (!current)
2957 return;
2958
2959 pushScriptElement(current);
2960}
2961
2962bool QQmlDomAstCreatorBase::visit(AST::NotExpression *)
2963{
2964 return m_enableScriptExpressions;
2965}
2966
2967void QQmlDomAstCreatorBase::endVisit(AST::NotExpression *statement)
2968{
2969 if (!m_enableScriptExpressions)
2970 return;
2971
2972 auto current =
2973 makeUnaryExpression(statement, statement->notToken, statement->expression, Prefix);
2974 if (!current)
2975 return;
2976
2977 pushScriptElement(current);
2978}
2979
2980bool QQmlDomAstCreatorBase::visit(AST::TypeOfExpression *)
2981{
2982 return m_enableScriptExpressions;
2983}
2984
2985void QQmlDomAstCreatorBase::endVisit(AST::TypeOfExpression *statement)
2986{
2987 if (!m_enableScriptExpressions)
2988 return;
2989
2990 auto current =
2991 makeUnaryExpression(statement, statement->typeofToken, statement->expression, Prefix);
2992 if (!current)
2993 return;
2994
2995 pushScriptElement(current);
2996}
2997
2998bool QQmlDomAstCreatorBase::visit(AST::DeleteExpression *)
2999{
3000 return m_enableScriptExpressions;
3001}
3002
3003void QQmlDomAstCreatorBase::endVisit(AST::DeleteExpression *statement)
3004{
3005 if (!m_enableScriptExpressions)
3006 return;
3007
3008 auto current =
3009 makeUnaryExpression(statement, statement->deleteToken, statement->expression, Prefix);
3010 if (!current)
3011 return;
3012
3013 pushScriptElement(current);
3014}
3015
3016bool QQmlDomAstCreatorBase::visit(AST::VoidExpression *)
3017{
3018 return m_enableScriptExpressions;
3019}
3020
3021void QQmlDomAstCreatorBase::endVisit(AST::VoidExpression *statement)
3022{
3023 if (!m_enableScriptExpressions)
3024 return;
3025
3026 auto current =
3027 makeUnaryExpression(statement, statement->voidToken, statement->expression, Prefix);
3028 if (!current)
3029 return;
3030
3031 pushScriptElement(current);
3032}
3033
3034bool QQmlDomAstCreatorBase::visit(AST::PostDecrementExpression *)
3035{
3036 return m_enableScriptExpressions;
3037}
3038
3039void QQmlDomAstCreatorBase::endVisit(AST::PostDecrementExpression *statement)
3040{
3041 if (!m_enableScriptExpressions)
3042 return;
3043
3044 auto current =
3045 makeUnaryExpression(statement, statement->decrementToken, statement->base, Postfix);
3046 if (!current)
3047 return;
3048
3049 pushScriptElement(current);
3050}
3051
3052bool QQmlDomAstCreatorBase::visit(AST::PostIncrementExpression *)
3053{
3054 return m_enableScriptExpressions;
3055}
3056
3057void QQmlDomAstCreatorBase::endVisit(AST::PostIncrementExpression *statement)
3058{
3059 if (!m_enableScriptExpressions)
3060 return;
3061
3062 auto current =
3063 makeUnaryExpression(statement, statement->incrementToken, statement->base, Postfix);
3064 if (!current)
3065 return;
3066
3067 pushScriptElement(current);
3068}
3069
3070bool QQmlDomAstCreatorBase::visit(AST::PreIncrementExpression *)
3071{
3072 return m_enableScriptExpressions;
3073}
3074
3075void QQmlDomAstCreatorBase::endVisit(AST::PreIncrementExpression *statement)
3076{
3077 if (!m_enableScriptExpressions)
3078 return;
3079
3080 auto current = makeUnaryExpression(statement, statement->incrementToken, statement->expression,
3081 Prefix);
3082 if (!current)
3083 return;
3084
3085 pushScriptElement(current);
3086}
3087
3088bool QQmlDomAstCreatorBase::visit(AST::EmptyStatement *)
3089{
3090 return m_enableScriptExpressions;
3091}
3092
3093void QQmlDomAstCreatorBase::endVisit(AST::EmptyStatement *statement)
3094{
3095 if (!m_enableScriptExpressions)
3096 return;
3097
3098 auto current = makeGenericScriptElement(statement, DomType::ScriptEmptyStatement);
3099 current->addLocation(FileLocationRegion::SemicolonTokenRegion, statement->semicolonToken);
3100 pushScriptElement(current);
3101}
3102
3103bool QQmlDomAstCreatorBase::visit(AST::NestedExpression *)
3104{
3105 return m_enableScriptExpressions;
3106}
3107
3108void QQmlDomAstCreatorBase::endVisit(AST::NestedExpression *expression)
3109{
3110 if (!m_enableScriptExpressions)
3111 return;
3112
3113 auto current = makeGenericScriptElement(expression, DomType::ScriptParenthesizedExpression);
3114 current->addLocation(FileLocationRegion::LeftParenthesisRegion, expression->lparenToken);
3115 current->addLocation(FileLocationRegion::RightParenthesisRegion, expression->rparenToken);
3116
3117 if (expression->expression) {
3119 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
3120 removeCurrentScriptNode({});
3121 }
3122
3123 pushScriptElement(current);
3124}
3125
3126bool QQmlDomAstCreatorBase::visit(AST::NewExpression *)
3127{
3128 return m_enableScriptExpressions;
3129}
3130
3131void QQmlDomAstCreatorBase::endVisit(AST::NewExpression *expression)
3132{
3133 if (!m_enableScriptExpressions)
3134 return;
3135
3136 auto current = makeGenericScriptElement(expression, DomType::ScriptNewExpression);
3137 current->addLocation(FileLocationRegion::NewKeywordRegion, expression->newToken);
3138
3139 if (expression->expression) {
3141 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
3142 removeCurrentScriptNode({});
3143 }
3144
3145 pushScriptElement(current);
3146}
3147
3148bool QQmlDomAstCreatorBase::visit(AST::NewMemberExpression *)
3149{
3150 return m_enableScriptExpressions;
3151}
3152
3153void QQmlDomAstCreatorBase::endVisit(AST::NewMemberExpression *expression)
3154{
3155 if (!m_enableScriptExpressions)
3156 return;
3157
3158 auto current = makeGenericScriptElement(expression, DomType::ScriptNewMemberExpression);
3159 current->addLocation(FileLocationRegion::NewKeywordRegion, expression->newToken);
3160 current->addLocation(FileLocationRegion::LeftParenthesisRegion, expression->lparenToken);
3161 current->addLocation(FileLocationRegion::RightParenthesisRegion, expression->rparenToken);
3162
3163 if (expression->arguments) {
3165 current->insertChild(Fields::arguments, scriptNodeStack.takeLast().takeList());
3166 }
3167 if (expression->base) {
3169 current->insertChild(Fields::base, scriptNodeStack.takeLast().takeVariant());
3170 }
3171
3172 pushScriptElement(current);
3173}
3174
3175void QQmlDomAstCreatorBase::endVisit(AST::WithStatement *ast)
3176{
3177 if (!m_enableScriptExpressions)
3178 return;
3179
3180 auto current = makeGenericScriptElement(ast, DomType::ScriptWithStatement);
3181
3182 if (ast->statement) {
3184 current->insertChild(Fields::statement, scriptNodeStack.takeLast().takeVariant());
3185 }
3186 if (ast->expression) {
3188 current->insertChild(Fields::expression, scriptNodeStack.takeLast().takeVariant());
3189 }
3190
3191 pushScriptElement(current);
3192}
3193
3194bool QQmlDomAstCreatorBase::visit(AST::PreDecrementExpression *)
3195{
3196 return m_enableScriptExpressions;
3197}
3198
3199void QQmlDomAstCreatorBase::endVisit(AST::PreDecrementExpression *statement)
3200{
3201 if (!m_enableScriptExpressions)
3202 return;
3203
3204 auto current = makeUnaryExpression(statement, statement->decrementToken, statement->expression,
3205 Prefix);
3206 if (!current)
3207 return;
3208
3209 pushScriptElement(current);
3210}
3211
3213 QQmlJSLogger *logger,
3214 QQmlJSImporter *importer)
3215 : m_logger(logger),
3221{
3222}
3223
3224#define X(name)
3225 bool QQmlDomAstCreatorWithQQmlJSScope::visit(name *node)
3226 {
3227 return visitT(node);
3228 }
3229 void QQmlDomAstCreatorWithQQmlJSScope::endVisit(name *node)
3230 {
3231 endVisitT(node);
3232 }
3233QQmlJSASTClassListToVisit
3234#undef X
3235
3236void QQmlDomAstCreatorWithQQmlJSScope::setScopeInDomAfterEndvisit()
3237{
3238 const QQmlJSScope::ConstPtr scope = m_scopeCreator.m_currentScope;
3239 if (!m_domCreator.scriptNodeStack.isEmpty()) {
3240 auto topOfStack = m_domCreator.currentScriptNodeEl();
3241 switch (topOfStack.kind) {
3243 // A ScriptIdentifierExpression in a QML scope is an actual ID.
3244 if (scope->scopeType() == QQmlJSScope::ScopeType::QMLScope)
3245 m_domCreator.currentScriptNodeEl().setSemanticScope(scope);
3246 break;
3247 }
3248 case DomType::ScriptBlockStatement:
3249 case DomType::ScriptForStatement:
3250 case DomType::ScriptForEachStatement:
3251 case DomType::ScriptDoWhileStatement:
3252 case DomType::ScriptWhileStatement:
3253 case DomType::List:
3254 m_domCreator.currentScriptNodeEl().setSemanticScope(scope);
3255 break;
3257 // Put the body's scope into the function expression: function expressions will contain
3258 // their parents scope instead of their own without this
3259 auto element = m_domCreator.currentScriptNodeEl().value;
3260 auto scriptElementVariant = std::get_if<ScriptElementVariant>(&element);
3261 if (!scriptElementVariant || !scriptElementVariant->data())
3262 break;
3263 scriptElementVariant->visit([](auto &&e) {
3264 using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
3265 if (e->kind() != DomType::ScriptFunctionExpression)
3266 return;
3267
3268 if constexpr (std::is_same_v<U,
3269 ScriptElement::PointerType<
3270 ScriptElements::GenericScriptElement>>) {
3271 if (auto bodyPtr = e->elementChild(Fields::body)) {
3272 const auto bodyScope = bodyPtr.base()->semanticScope();
3273 e->setSemanticScope(bodyScope);
3274 }
3275 }
3276 });
3277 break;
3278 }
3279
3280 // TODO: find which script elements also have a scope and implement them here
3281 default:
3282 break;
3283 };
3284 } else if (!m_domCreator.nodeStack.isEmpty()) {
3285 std::visit(
3286 [&scope](auto &&e) {
3287 using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
3288 // TODO: find which dom elements also have a scope and implement them here
3289 if constexpr (std::is_same_v<U, QmlObject>) {
3290 e.setSemanticScope(scope);
3291 } else if constexpr (std::is_same_v<U, QmlComponent>) {
3292 e.setSemanticScope(scope);
3293 } else if constexpr (std::is_same_v<U, MethodInfo>) {
3294 if (e.body) {
3295 if (auto scriptElement = e.body->scriptElement()) {
3296 scriptElement.base()->setSemanticScope(scope);
3297 }
3298 }
3299 e.setSemanticScope(scope);
3300 }
3301 },
3302 m_domCreator.currentNodeEl().item.value);
3303 }
3304}
3305
3306void QQmlDomAstCreatorWithQQmlJSScope::setScopeInDomBeforeEndvisit()
3307{
3308 const QQmlJSScope::ConstPtr scope = m_scopeCreator.m_currentScope;
3309
3310 // depending whether the property definition has a binding, the property definition might be
3311 // either at the last position in the stack or at the position before the last position.
3312 if (m_domCreator.nodeStack.size() > 1
3313 && m_domCreator.nodeStack.last().item.kind == DomType::Binding) {
3314 std::visit(
3315 [&scope](auto &&e) {
3316 using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
3317 if constexpr (std::is_same_v<U, PropertyDefinition>) {
3318 // Make sure to use the property definition scope instead of the binding
3319 // scope. If the current scope is a binding scope (this happens when the
3320 // property definition has a binding, like `property int i: 45` for
3321 // example), then the property definition scope is the parent of the current
3322 // scope.
3323 const bool usePropertyDefinitionScopeInsteadOfTheBindingScope =
3324 QQmlSA::isFunctionScope(scope->scopeType())
3325 && scope->parentScope()
3326 && scope->parentScope()->scopeType() == QQmlSA::ScopeType::QMLScope;
3327 e.setSemanticScope(usePropertyDefinitionScopeInsteadOfTheBindingScope
3328 ? scope->parentScope()
3329 : scope);
3330 }
3331 },
3332 m_domCreator.currentNodeEl(1).item.value);
3333 }
3334 if (m_domCreator.nodeStack.size() > 0) {
3335 std::visit(
3336 [&scope](auto &&e) {
3337 using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
3338 if constexpr (std::is_same_v<U, PropertyDefinition>) {
3339 e.setSemanticScope(scope);
3340 Q_ASSERT(e.semanticScope());
3341 } else if constexpr (std::is_same_v<U, MethodInfo>) {
3342 if (e.methodType == MethodInfo::Signal) {
3343 e.setSemanticScope(scope);
3344 }
3345 }
3346 },
3347 m_domCreator.currentNodeEl().item.value);
3348 }
3349}
3350
3352{
3353}
3354
3355void QQmlDomAstCreatorWithQQmlJSScope::loadQmlDependenciesInDom()
3356{
3357 const auto types = m_scopeCreator.imports().types();
3358 auto environment = m_domCreator.currentEnvironment();
3359 for (auto it = types.begin(), end = types.end(); it != end; ++it) {
3360 auto scope = it->scope;
3361 if (!scope)
3362 continue;
3363 auto factory = scope.factory();
3364 if (!factory && !scope->isComposite())
3365 continue;
3366 const QString filePath = QQmlJSUtils::qmlSourcePathFromBuildPath(
3367 m_importer->resourceFileMapper(),
3368 factory ? factory->filePath() : scope->filePath());
3369 if (!environment->hasFile(filePath))
3370 environment->loadFile(FileToLoad::fromFileSystem(environment, filePath), { });
3371 }
3372}
3373
3374#define X(name)
3375 bool QQmlDomAstCreator::visit(AST::name *ast)
3376 {
3377 return QQmlDomAstCreatorBase::visit(ast) && visitWithCustomListIteration(ast, this);
3378 }
3380#undef X
3381
3382} // end namespace Dom
3383} // end namespace QQmlJS
3384
3385#undef Q_SCRIPTELEMENT_DISABLE
3386#undef Q_SCRIPTELEMENT_EXIT_IF
3387
3388QT_END_NAMESPACE
Binding & operator=(const Binding &)
void setBindingIdentifiers(const ScriptElementVariant &bindingIdentifiers)
std::shared_ptr< ScriptExpression > scriptExpressionValue()
Path addValue(const EnumItem &value)
Represents a set of tags grouping a set of related error messages.
TypeAnnotationStyle typeAnnotationStyle
PathEls::Kind Kind
Path operator[](int i) const
Path mid(int offset, int length) const
Path last() const
Kind headKind() const
void setNameIdentifiers(const ScriptElementVariant &name)
QQmlDomAstCreatorBase(const MutableDomItem &qmlFile)
void endVisit(AST::UiProgram *) override
void throwRecursionDepthError() override
void endVisitHelper(AST::PatternElement *pe, const std::shared_ptr< ScriptElements::GenericScriptElement > &element)
bool visit(AST::UiProgram *program) override
virtual QQmlJSASTClassListToVisit void throwRecursionDepthError() override
QQmlDomAstCreatorWithQQmlJSScope(MutableDomItem &qmlFile, QQmlJSLogger *logger, QQmlJSImporter *importer)
void setNameIdentifiers(const ScriptElementVariant &name)
Path addId(const Id &id, AddOption option=AddOption::Overwrite, Id **idPtr=nullptr)
void setNameIdentifiers(const ScriptElementVariant &name)
void updatePathFromOwner(const Path &newPath) override
Path addPrototypePath(const Path &prototypePath)
QList< std::pair< SourceLocation, DomItem > > Attributes
Use this to contain any script element.
void replaceKindForGenericChildren(DomType oldType, DomType newType)
Provides entities to maintain mappings between elements and their location in a file.
void addRegion(const Tree &fLoc, FileLocationRegion region, SourceLocation loc)
Tree ensure(const Tree &base, const Path &basePath)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
#define Q_SCRIPTELEMENT_EXIT_IF(check)
#define Q_SCRIPTELEMENT_DISABLE()
#define NewErrorGroup(name)