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