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 const auto m = "id attributes should only be a lower case letter or an underscore "
1164 "followed by letters, numbers or underscores, not %1";
1165 qmlFile.addError(std::move(
1166 astParseErrors().warning(tr(m).arg(iExp->name)).withPath(pathFromOwner)));
1167 }
1168 } else {
1169 pathFromOwner =
1170 current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
1171 Q_ASSERT_X(bindingPtr, className, "binding could not be retrieved");
1172 const auto m = "id attributes should only be a lower case letter or an underscore "
1173 "followed by letters, numbers or underscores, not %1 %2, assuming they "
1174 "refer to a property";
1175 qmlFile.addError(std::move(
1176 astParseErrors()
1177 .warning(tr(m).arg(script->code(), script->astRelocatableDump()))
1178 .withPath(pathFromOwner)));
1179 }
1180 } else {
1181 pathFromOwner =
1182 current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
1183 QmlStackElement &containingObjectEl = currentEl<QmlObject>();
1184 // remove the containingObjectEl.path prefix from pathFromOwner
1185 Path pathFromContainingObject = pathFromOwner.mid(containingObjectEl.path.length());
1186 auto bindingFileLocation =
1187 FileLocations::ensure(containingObjectEl.fileLocations, pathFromContainingObject);
1188 FileLocations::addRegion(bindingFileLocation, IdentifierRegion,
1189 el->qualifiedId->identifierToken);
1190 FileLocations::addRegion(bindingFileLocation, ColonTokenRegion, el->colonToken);
1191
1192 setBindingIdentifiers(pathFromOwner, el->qualifiedId, bindingPtr);
1193
1194 Q_ASSERT_X(bindingPtr, className, "binding could not be retrieved");
1195 }
1196 if (bindingPtr)
1197 pushEl(pathFromOwner, *bindingPtr, el);
1198 else if (idPtr)
1199 pushEl(pathFromOwner, *idPtr, el);
1200 else
1201 Q_UNREACHABLE();
1202 loadAnnotations(el);
1203 // avoid duplicate colon location for id?
1204 FileLocations::addRegion(nodeStack.last().fileLocations, ColonTokenRegion, el->colonToken);
1205 return true;
1206}
1207
1208void QQmlDomAstCreatorBase::setScriptExpression (const std::shared_ptr<ScriptExpression>& value)
1209{
1210 if (m_enableScriptExpressions
1211 && (scriptNodeStack.size() != 1 || currentScriptNodeEl().isList()))
1213 if (m_enableScriptExpressions) {
1214 FileLocations::Tree valueLoc = FileLocations::ensure(currentNodeEl().fileLocations,
1215 Path().withField(Fields::value));
1216 value->setScriptElement(finalizeScriptExpression(currentScriptNodeEl().takeVariant(),
1217 Path().withField(Fields::scriptElement),
1218 valueLoc));
1219 removeCurrentScriptNode({});
1220 }
1221};
1222
1223void QQmlDomAstCreatorBase::endVisit(AST::UiScriptBinding *)
1224{
1225 --m_nestedFunctionDepth;
1226 DomValue &lastEl = currentNode();
1227 index_type idx = currentIndex();
1228 if (lastEl.kind == DomType::Binding) {
1229 Binding &b = std::get<Binding>(lastEl.value);
1230
1231 setScriptExpression(b.scriptExpressionValue());
1232
1233 QmlObject &containingObject = current<QmlObject>();
1234 Binding *bPtr = valueFromMultimap(containingObject.m_bindings, b.name(), idx);
1235 Q_ASSERT(bPtr);
1236 *bPtr = b;
1237 } else if (lastEl.kind == DomType::Id) {
1238 Id &id = std::get<Id>(lastEl.value);
1239
1240 setScriptExpression(id.value);
1241
1242 QmlComponent &comp = current<QmlComponent>();
1243 Id *idPtr = valueFromMultimap(comp.m_ids, id.name, idx);
1244 *idPtr = id;
1245 } else {
1246 Q_UNREACHABLE();
1247 }
1248
1249 // there should be no more uncollected script elements
1250 if (m_enableScriptExpressions && !scriptNodeStack.empty()) {
1252 }
1253 removeCurrentNode({});
1254}
1255
1256bool QQmlDomAstCreatorBase::visit(AST::UiArrayBinding *el)
1257{
1258 QList<QmlObject> value;
1259 Binding bindingV(toString(el->qualifiedId), value, BindingType::Normal);
1260 Binding *bindingPtr;
1261 Path bindingPathFromOwner =
1262 current<QmlObject>().addBinding(bindingV, AddOption::KeepExisting, &bindingPtr);
1263 if (bindingV.name() == u"id")
1264 qmlFile.addError(std::move(
1265 astParseErrors()
1266 .error(tr("id attributes should have only simple strings as values"))
1267 .withPath(bindingPathFromOwner)));
1268
1269 setBindingIdentifiers(bindingPathFromOwner, el->qualifiedId, bindingPtr);
1270
1271 pushEl(bindingPathFromOwner, *bindingPtr, el);
1272 FileLocations::addRegion(currentNodeEl().fileLocations, ColonTokenRegion, el->colonToken);
1273 loadAnnotations(el);
1274 FileLocations::Tree arrayList =
1275 createMap(currentNodeEl().fileLocations, Path::fromField(Fields::value), nullptr);
1276 FileLocations::addRegion(arrayList, LeftBracketRegion, el->lbracketToken);
1277 FileLocations::addRegion(arrayList, RightBracketRegion, el->rbracketToken);
1278 arrayBindingLevels.append(nodeStack.size());
1279 return true;
1280}
1281
1282void QQmlDomAstCreatorBase::endVisit(AST::UiArrayBinding *)
1283{
1284 index_type idx = currentIndex();
1285 Binding &b = std::get<Binding>(currentNode().value);
1286 Binding *bPtr = valueFromMultimap(current<QmlObject>().m_bindings, b.name(), idx);
1287 *bPtr = b;
1288 arrayBindingLevels.removeLast();
1289 removeCurrentNode(DomType::Binding);
1290}
1291
1292void QQmlDomAstCreatorBase::endVisit(AST::ArgumentList *list)
1293{
1294 endVisitForLists(list);
1295}
1296
1297bool QQmlDomAstCreatorBase::visit(AST::UiParameterList *)
1298{
1299 return false; // do not create script node for Ui stuff
1300}
1301
1302void QQmlDomAstCreatorBase::endVisit(AST::PatternElementList *list)
1303{
1304 endVisitForLists<AST::PatternElementList>(list, [](AST::PatternElementList *current) {
1305 int toCollect = 0;
1306 toCollect += bool(current->elision);
1307 toCollect += bool(current->element);
1308 return toCollect;
1309 });
1310}
1311
1312void QQmlDomAstCreatorBase::endVisit(AST::PatternPropertyList *list)
1313{
1314 endVisitForLists(list);
1315}
1316
1317/*!
1318 \internal
1319 Implementing the logic of this method in \c QQmlDomAstCreator::visit(AST::UiQualifiedId *)
1320 would create scriptelements at places where there are not needed. This is mainly because
1321 UiQualifiedId's appears inside and outside of script parts.
1322*/
1323ScriptElementVariant QQmlDomAstCreatorBase::scriptElementForQualifiedId(AST::UiQualifiedId *expression)
1324{
1325 auto id = std::make_shared<ScriptElements::IdentifierExpression>(
1326 expression->firstSourceLocation(), expression->lastSourceLocation());
1327 id->setName(expression->toString());
1328
1329 return ScriptElementVariant::fromElement(id);
1330}
1331
1332bool QQmlDomAstCreatorBase::visit(AST::UiQualifiedId *)
1333{
1334 if (!m_enableScriptExpressions)
1335 return false;
1336
1337 return false;
1338}
1339
1340bool QQmlDomAstCreatorBase::visit(AST::UiEnumDeclaration *el)
1341{
1342 EnumDecl eDecl;
1343 eDecl.setName(el->name.toString());
1344 EnumDecl *ePtr;
1345 Path enumPathFromOwner =
1346 current<QmlComponent>().addEnumeration(eDecl, AddOption::KeepExisting, &ePtr);
1347 pushEl(enumPathFromOwner, *ePtr, el);
1348 FileLocations::addRegion(nodeStack.last().fileLocations, EnumKeywordRegion, el->enumToken);
1349 FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion, el->identifierToken);
1350 FileLocations::addRegion(nodeStack.last().fileLocations, LeftBraceRegion, el->lbraceToken);
1351 FileLocations::addRegion(nodeStack.last().fileLocations, RightBraceRegion, el->rbraceToken);
1352 loadAnnotations(el);
1353 return true;
1354}
1355
1356void QQmlDomAstCreatorBase::endVisit(AST::UiEnumDeclaration *)
1357{
1358 EnumDecl &e = std::get<EnumDecl>(currentNode().value);
1359 EnumDecl *ePtr =
1360 valueFromMultimap(current<QmlComponent>().m_enumerations, e.name(), currentIndex());
1361 Q_ASSERT(ePtr);
1362 *ePtr = e;
1363 removeCurrentNode(DomType::EnumDecl);
1364}
1365
1366bool QQmlDomAstCreatorBase::visit(AST::UiEnumMemberList *el)
1367{
1368 EnumItem it(el->member.toString(), el->value,
1369 el->valueToken.isValid() ? EnumItem::ValueKind::ExplicitValue
1371 EnumDecl &eDecl = std::get<EnumDecl>(currentNode().value);
1372 Path itPathFromDecl = eDecl.addValue(it);
1373 const auto map = createMap(DomType::EnumItem, itPathFromDecl, nullptr);
1374 if (el->commaToken.isValid())
1375 FileLocations::addRegion(map, CommaTokenRegion, el->commaToken);
1376 if (el->memberToken.isValid())
1377 FileLocations::addRegion(map, IdentifierRegion, el->memberToken);
1378 if (el->equalToken.isValid())
1379 FileLocations::addRegion(map, EqualTokenRegion, el->equalToken);
1380 if (el->valueToken.isValid())
1381 FileLocations::addRegion(map, EnumValueRegion, el->valueToken);
1383 map, MainRegion, combine(combine(el->memberToken, el->commaToken), el->valueToken));
1384 return true;
1385}
1386
1387void QQmlDomAstCreatorBase::endVisit(AST::UiEnumMemberList *el)
1388{
1389 Node::accept(el->next, this); // put other enum members at the same level as this one...
1390}
1391
1392bool QQmlDomAstCreatorBase::visit(AST::UiInlineComponent *el)
1393{
1394 QStringList els = current<QmlComponent>().name().split(QLatin1Char('.'));
1395 els.append(el->name.toString());
1396 QString cName = els.join(QLatin1Char('.'));
1397 QmlComponent *compPtr;
1398 Path p = qmlFilePtr->addComponent(QmlComponent(cName), AddOption::KeepExisting, &compPtr);
1399
1400 if (m_enableScriptExpressions) {
1401 auto inlineComponentType =
1402 makeGenericScriptElement(el->identifierToken, DomType::ScriptType);
1403
1404 auto typeName = std::make_shared<ScriptElements::IdentifierExpression>(el->identifierToken);
1405 typeName->setName(el->name);
1406 inlineComponentType->insertChild(Fields::typeName,
1407 ScriptElementVariant::fromElement(typeName));
1409 finalizeScriptExpression(ScriptElementVariant::fromElement(inlineComponentType),
1410 p.withField(Fields::nameIdentifiers), rootMap));
1411 }
1412
1413 pushEl(p, *compPtr, el);
1414 FileLocations::addRegion(nodeStack.last().fileLocations, ComponentKeywordRegion,
1415 el->componentToken);
1416 FileLocations::addRegion(nodeStack.last().fileLocations, IdentifierRegion, el->identifierToken);
1417 loadAnnotations(el);
1418 return true;
1419}
1420
1421void QQmlDomAstCreatorBase::endVisit(AST::UiInlineComponent *)
1422{
1423 QmlComponent &component = std::get<QmlComponent>(currentNode().value);
1424 QStringList nameEls = component.name().split(QChar::fromLatin1('.'));
1425 QString key = nameEls.mid(1).join(QChar::fromLatin1('.'));
1426 QmlComponent *cPtr = valueFromMultimap(qmlFilePtr->lazyMembers().m_components, key, currentIndex());
1427 Q_ASSERT(cPtr);
1428 *cPtr = component;
1429 removeCurrentNode(DomType::QmlComponent);
1430}
1431
1432bool QQmlDomAstCreatorBase::visit(UiRequired *el)
1433{
1434 PropertyDefinition pDef;
1435 pDef.name = el->name.toString();
1436 pDef.isRequired = true;
1437 PropertyDefinition *pDefPtr;
1438 Path pathFromOwner =
1439 current<QmlObject>().addPropertyDef(pDef, AddOption::KeepExisting, &pDefPtr);
1440 createMap(DomType::PropertyDefinition, pathFromOwner, el);
1441 return false;
1442}
1443
1444bool QQmlDomAstCreatorBase::visit(AST::UiAnnotation *el)
1445{
1446 QmlObject a;
1447 a.setName(QStringLiteral(u"@") + toString(el->qualifiedTypeNameId));
1448 // add annotation prototype?
1449 DomValue &containingElement = currentNode();
1450 Path pathFromOwner;
1451 QmlObject *aPtr = nullptr;
1452 switch (containingElement.kind) {
1453 case DomType::QmlObject:
1454 pathFromOwner = std::get<QmlObject>(containingElement.value).addAnnotation(a, &aPtr);
1455 break;
1456 case DomType::Binding:
1457 pathFromOwner = std::get<Binding>(containingElement.value)
1458 .addAnnotation(currentNodeEl().path, a, &aPtr);
1459 break;
1460 case DomType::Id:
1461 pathFromOwner =
1462 std::get<Id>(containingElement.value).addAnnotation(currentNodeEl().path, a, &aPtr);
1463 break;
1465 pathFromOwner = std::get<PropertyDefinition>(containingElement.value)
1466 .addAnnotation(currentNodeEl().path, a, &aPtr);
1467 break;
1469 pathFromOwner = std::get<MethodInfo>(containingElement.value)
1470 .addAnnotation(currentNodeEl().path, a, &aPtr);
1471 break;
1472 default:
1473 qCWarning(domLog) << "Unexpected container object for annotation:"
1474 << domTypeToString(containingElement.kind);
1475 Q_UNREACHABLE();
1476 }
1477 pushEl(pathFromOwner, *aPtr, el);
1478 return true;
1479}
1480
1481void QQmlDomAstCreatorBase::endVisit(AST::UiAnnotation *)
1482{
1483 DomValue &containingElement = currentNode(1);
1484 Path pathFromOwner;
1485 QmlObject &a = std::get<QmlObject>(currentNode().value);
1486 switch (containingElement.kind) {
1487 case DomType::QmlObject:
1488 std::get<QmlObject>(containingElement.value).m_annotations[currentIndex()] = a;
1489 break;
1490 case DomType::Binding:
1491 std::get<Binding>(containingElement.value).m_annotations[currentIndex()] = a;
1492 break;
1493 case DomType::Id:
1494 std::get<Id>(containingElement.value).annotations[currentIndex()] = a;
1495 break;
1496 case DomType::PropertyDefinition:
1497 std::get<PropertyDefinition>(containingElement.value).annotations[currentIndex()] = a;
1498 break;
1499 case DomType::MethodInfo:
1500 std::get<MethodInfo>(containingElement.value).annotations[currentIndex()] = a;
1501 break;
1502 default:
1503 Q_UNREACHABLE();
1504 }
1505 removeCurrentNode(DomType::QmlObject);
1506}
1507
1509{
1510 qmlFile.addError(astParseErrors().error(
1511 tr("Maximum statement or expression depth exceeded in QmlDomAstCreator")));
1512}
1513
1514void QQmlDomAstCreatorBase::endVisit(AST::StatementList *list)
1515{
1516 endVisitForLists(list);
1517}
1518
1519bool QQmlDomAstCreatorBase::visit(AST::BinaryExpression *)
1520{
1521 if (!m_enableScriptExpressions)
1522 return false;
1523
1524 return true;
1525}
1526
1527void QQmlDomAstCreatorBase::endVisit(AST::BinaryExpression *exp)
1528{
1529 if (!m_enableScriptExpressions)
1530 return;
1531
1532 auto current = makeScriptElement<ScriptElements::BinaryExpression>(exp);
1533 current->addLocation(OperatorTokenRegion, exp->operatorToken);
1535 current->setRight(currentScriptNodeEl().takeVariant());
1536 removeCurrentScriptNode({});
1538 current->setLeft(currentScriptNodeEl().takeVariant());
1539 removeCurrentScriptNode({});
1540
1541 pushScriptElement(current);
1542}
1543
1544bool QQmlDomAstCreatorBase::visit(AST::Block *)
1545{
1546 if (!m_enableScriptExpressions)
1547 return false;
1548
1549 return true;
1550}
1551
1552void QQmlDomAstCreatorBase::endVisit(AST::Block *block)
1553{
1554 if (!m_enableScriptExpressions)
1555 return;
1556
1557 auto current = makeScriptElement<ScriptElements::BlockStatement>(block);
1558
1559 if (block->statements) {
1561 current->setStatements(currentScriptNodeEl().takeList());
1562 removeCurrentScriptNode(DomType::List);
1563 }
1564
1565 pushScriptElement(current);
1566}
1567
1568bool QQmlDomAstCreatorBase::visit(AST::ForStatement *)
1569{
1570 if (!m_enableScriptExpressions)
1571 return false;
1572
1573 return true;
1574}
1575
1576void QQmlDomAstCreatorBase::endVisit(AST::ForStatement *forStatement)
1577{
1578 if (!m_enableScriptExpressions)
1579 return;
1580
1581 auto current = makeScriptElement<ScriptElements::ForStatement>(forStatement);
1582 current->addLocation(FileLocationRegion::ForKeywordRegion, forStatement->forToken);
1583 current->addLocation(FileLocationRegion::LeftParenthesisRegion, forStatement->lparenToken);
1584 current->addLocation(FileLocationRegion::FirstSemicolonTokenRegion,
1585 forStatement->firstSemicolonToken);
1586 current->addLocation(FileLocationRegion::SecondSemicolonRegion,
1587 forStatement->secondSemicolonToken);
1588 current->addLocation(FileLocationRegion::RightParenthesisRegion, forStatement->rparenToken);
1589
1590 if (forStatement->statement) {
1592 current->setBody(currentScriptNodeEl().takeVariant());
1593 removeCurrentScriptNode(std::nullopt);
1594 }
1595
1596 if (forStatement->expression) {
1598 current->setExpression(currentScriptNodeEl().takeVariant());
1599 removeCurrentScriptNode(std::nullopt);
1600 }
1601
1602 if (forStatement->condition) {
1604 current->setCondition(currentScriptNodeEl().takeVariant());
1605 removeCurrentScriptNode(std::nullopt);
1606 }
1607
1608 if (forStatement->declarations) {
1610 auto variableDeclaration = makeGenericScriptElement(forStatement->declarations,
1612
1613 ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
1616 variableDeclaration->insertChild(Fields::declarations, std::move(list));
1617 removeCurrentScriptNode({});
1618
1619 current->setDeclarations(ScriptElementVariant::fromElement(variableDeclaration));
1620
1621 if (auto pe = forStatement->declarations->declaration;
1622 pe && pe->declarationKindToken.isValid()) {
1623 current->addLocation(FileLocationRegion::TypeIdentifierRegion,
1624 pe->declarationKindToken);
1625 }
1626 }
1627
1628 if (forStatement->initialiser) {
1630 current->setInitializer(currentScriptNodeEl().takeVariant());
1631 removeCurrentScriptNode(std::nullopt);
1632 }
1633 pushScriptElement(current);
1634}
1635
1636bool QQmlDomAstCreatorBase::visit(AST::IdentifierExpression *expression)
1637{
1638 if (!m_enableScriptExpressions)
1639 return false;
1640
1641 auto current = makeScriptElement<ScriptElements::IdentifierExpression>(expression);
1642 current->setName(expression->name);
1643 pushScriptElement(current);
1644 return true;
1645}
1646
1647bool QQmlDomAstCreatorBase::visit(AST::NumericLiteral *expression)
1648{
1649 if (!m_enableScriptExpressions)
1650 return false;
1651
1652 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1653 current->setLiteralValue(expression->value);
1654 pushScriptElement(current);
1655 return true;
1656}
1657
1658bool QQmlDomAstCreatorBase::visit(AST::StringLiteral *expression)
1659{
1660 if (!m_enableScriptExpressions)
1661 return false;
1662
1663 pushScriptElement(makeStringLiteral(expression->value, expression));
1664 return true;
1665}
1666
1667bool QQmlDomAstCreatorBase::visit(AST::NullExpression *expression)
1668{
1669 if (!m_enableScriptExpressions)
1670 return false;
1671
1672 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1673 current->setLiteralValue(nullptr);
1674 pushScriptElement(current);
1675 return true;
1676}
1677
1678bool QQmlDomAstCreatorBase::visit(AST::TrueLiteral *expression)
1679{
1680 if (!m_enableScriptExpressions)
1681 return false;
1682
1683 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1684 current->setLiteralValue(true);
1685 pushScriptElement(current);
1686 return true;
1687}
1688
1689bool QQmlDomAstCreatorBase::visit(AST::FalseLiteral *expression)
1690{
1691 if (!m_enableScriptExpressions)
1692 return false;
1693
1694 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1695 current->setLiteralValue(false);
1696 pushScriptElement(current);
1697 return true;
1698}
1699
1700bool QQmlDomAstCreatorBase::visit(AST::IdentifierPropertyName *expression)
1701{
1702 if (!m_enableScriptExpressions)
1703 return false;
1704
1705 auto current = makeScriptElement<ScriptElements::IdentifierExpression>(expression);
1706 current->setName(expression->id);
1707 pushScriptElement(current);
1708 return true;
1709}
1710
1711bool QQmlDomAstCreatorBase::visit(AST::StringLiteralPropertyName *expression)
1712{
1713 if (!m_enableScriptExpressions)
1714 return false;
1715
1716 pushScriptElement(makeStringLiteral(expression->id, expression));
1717 return true;
1718}
1719
1720bool QQmlDomAstCreatorBase::visit(AST::TypeAnnotation *)
1721{
1722 if (!m_enableScriptExpressions)
1723 return false;
1724
1725 // do nothing: the work is done in (end)visit(AST::Type*).
1726 return true;
1727}
1728
1729bool QQmlDomAstCreatorBase::visit(AST::RegExpLiteral *literal)
1730{
1731 if (!m_enableScriptExpressions)
1732 return false;
1733
1734 auto current = makeGenericScriptElement(literal, DomType::ScriptRegExpLiteral);
1735 current->insertValue(Fields::regExpPattern, literal->pattern);
1736 current->insertValue(Fields::regExpFlags, literal->flags);
1737 pushScriptElement(current);
1738
1739 return true;
1740}
1741
1742bool QQmlDomAstCreatorBase::visit(AST::ThisExpression *expression)
1743{
1744 if (!m_enableScriptExpressions)
1745 return false;
1746
1747 auto current = makeGenericScriptElement(expression, DomType::ScriptThisExpression);
1748 if (expression->thisToken.isValid())
1749 current->addLocation(ThisKeywordRegion, expression->thisToken);
1750 pushScriptElement(current);
1751 return true;
1752}
1753
1754bool QQmlDomAstCreatorBase::visit(AST::SuperLiteral *expression)
1755{
1756 if (!m_enableScriptExpressions)
1757 return false;
1758
1759 auto current = makeGenericScriptElement(expression, DomType::ScriptSuperLiteral);
1760 if (expression->superToken.isValid())
1761 current->addLocation(SuperKeywordRegion, expression->superToken);
1762 pushScriptElement(current);
1763 return true;
1764}
1765
1766bool QQmlDomAstCreatorBase::visit(AST::NumericLiteralPropertyName *expression)
1767{
1768 if (!m_enableScriptExpressions)
1769 return false;
1770
1771 auto current = makeScriptElement<ScriptElements::Literal>(expression);
1772 current->setLiteralValue(expression->id);
1773 pushScriptElement(current);
1774 return true;
1775}
1776
1777bool QQmlDomAstCreatorBase::visit(AST::ComputedPropertyName *)
1778{
1779 if (!m_enableScriptExpressions)
1780 return false;
1781
1782 // nothing to do, just forward the underlying expression without changing/wrapping it
1783 return true;
1784}
1785
1786template<typename T>
1787void QQmlDomAstCreatorBase::endVisitForLists(T *list,
1788 const std::function<int(T *)> &scriptElementsPerEntry)
1789{
1790 if (!m_enableScriptExpressions)
1791 return;
1792
1793 auto current = makeScriptList(list);
1794 for (auto it = list; it; it = it->next) {
1795 const int entriesToCollect = scriptElementsPerEntry ? scriptElementsPerEntry(it) : 1;
1796 for (int i = 0; i < entriesToCollect; ++i) {
1797 Q_SCRIPTELEMENT_EXIT_IF(scriptNodeStack.isEmpty());
1798 auto last = scriptNodeStack.takeLast();
1799 if (last.isList())
1800 current.append(last.takeList());
1801 else
1802 current.append(last.takeVariant());
1803 }
1804 }
1805
1806 current.reverse();
1807 pushScriptElement(current);
1808}
1809
1810void QQmlDomAstCreatorBase::endVisit(AST::VariableDeclarationList *list)
1811{
1812 endVisitForLists(list);
1813}
1814
1815bool QQmlDomAstCreatorBase::visit(AST::Elision *list)
1816{
1817 if (!m_enableScriptExpressions)
1818 return false;
1819
1820 auto currentList = makeScriptList(list);
1821
1822 for (auto it = list; it; it = it->next) {
1823 auto current = makeGenericScriptElement(it->commaToken, DomType::ScriptElision);
1824 currentList.append(ScriptElementVariant::fromElement(current));
1825 }
1826 pushScriptElement(currentList);
1827
1828 return false; // return false because we already iterated over the children using the custom
1829 // iteration above
1830}
1831
1832bool QQmlDomAstCreatorBase::visit(AST::PatternElement *)
1833{
1834 if (!m_enableScriptExpressions)
1835 return false;
1836
1837 return true;
1838}
1839
1840/*!
1841 \internal
1842 Avoid code-duplication, reuse this code when doing endVisit on types inheriting from
1843 AST::PatternElement.
1844*/
1846 AST::PatternElement *pe,
1847 const std::shared_ptr<ScriptElements::GenericScriptElement> &current)
1848{
1849 if (pe->equalToken.isValid())
1850 current->addLocation(FileLocationRegion::EqualTokenRegion, pe->equalToken);
1851
1852 if (pe->identifierToken.isValid() && !pe->bindingIdentifier.isEmpty()) {
1853 auto identifier =
1854 std::make_shared<ScriptElements::IdentifierExpression>(pe->identifierToken);
1855 identifier->setName(pe->bindingIdentifier);
1856 current->insertChild(Fields::identifier, ScriptElementVariant::fromElement(identifier));
1857 }
1858 if (pe->initializer) {
1860 current->insertChild(Fields::initializer, scriptNodeStack.last().takeVariant());
1861 scriptNodeStack.removeLast();
1862 }
1863 if (pe->typeAnnotation) {
1865 current->insertChild(Fields::type, scriptNodeStack.last().takeVariant());
1866 scriptNodeStack.removeLast();
1867 }
1868 if (pe->bindingTarget) {
1870 current->insertChild(Fields::bindingElement, scriptNodeStack.last().takeVariant());
1871 scriptNodeStack.removeLast();
1872 }
1873}
1874
1875void QQmlDomAstCreatorBase::endVisit(AST::PatternElement *pe)
1876{
1877 if (!m_enableScriptExpressions)
1878 return;
1879
1880 auto element = makeGenericScriptElement(pe, DomType::ScriptPattern);
1881 endVisitHelper(pe, element);
1882 // check if helper disabled scriptexpressions
1883 if (!m_enableScriptExpressions)
1884 return;
1885
1886 pushScriptElement(element);
1887}
1888
1889bool QQmlDomAstCreatorBase::visit(AST::IfStatement *)
1890{
1891 if (!m_enableScriptExpressions)
1892 return false;
1893
1894 return true;
1895}
1896
1897void QQmlDomAstCreatorBase::endVisit(AST::IfStatement *ifStatement)
1898{
1899 if (!m_enableScriptExpressions)
1900 return;
1901
1902 auto current = makeScriptElement<ScriptElements::IfStatement>(ifStatement);
1903 current->addLocation(LeftParenthesisRegion, ifStatement->lparenToken);
1904 current->addLocation(RightParenthesisRegion, ifStatement->rparenToken);
1905 current->addLocation(ElseKeywordRegion, ifStatement->elseToken);
1906 current->addLocation(IfKeywordRegion, ifStatement->ifToken);
1907
1908 if (ifStatement->ko) {
1910 current->setAlternative(scriptNodeStack.last().takeVariant());
1911 scriptNodeStack.removeLast();
1912 }
1913
1914 if (ifStatement->ok) {
1916 current->setConsequence(scriptNodeStack.last().takeVariant());
1917 scriptNodeStack.removeLast();
1918 }
1919 if (ifStatement->expression) {
1921 current->setCondition(scriptNodeStack.last().takeVariant());
1922 scriptNodeStack.removeLast();
1923 }
1924
1925 pushScriptElement(current);
1926}
1927
1928bool QQmlDomAstCreatorBase::visit(AST::ReturnStatement *)
1929{
1930 if (!m_enableScriptExpressions)
1931 return false;
1932
1933 return true;
1934}
1935
1936void QQmlDomAstCreatorBase::endVisit(AST::ReturnStatement *returnStatement)
1937{
1938 if (!m_enableScriptExpressions)
1939 return;
1940
1941 auto current = makeScriptElement<ScriptElements::ReturnStatement>(returnStatement);
1942 current->addLocation(ReturnKeywordRegion, returnStatement->returnToken);
1943
1944 if (returnStatement->expression) {
1946 current->setExpression(currentScriptNodeEl().takeVariant());
1947 removeCurrentScriptNode({});
1948 }
1949
1950 pushScriptElement(current);
1951}
1952
1953bool QQmlDomAstCreatorBase::visit(AST::YieldExpression *)
1954{
1955 if (!m_enableScriptExpressions)
1956 return false;
1957
1958 return true;
1959}
1960
1961void QQmlDomAstCreatorBase::endVisit(AST::YieldExpression *yExpression)
1962{
1963 if (!m_enableScriptExpressions)
1964 return;
1965
1966 auto current = makeGenericScriptElement(yExpression, DomType::ScriptYieldExpression);
1967 current->addLocation(YieldKeywordRegion, yExpression->yieldToken);
1968
1969 if (yExpression->expression) {
1971 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
1972 removeCurrentScriptNode({});
1973 }
1974
1975 pushScriptElement(current);
1976}
1977
1978bool QQmlDomAstCreatorBase::visit(AST::FieldMemberExpression *)
1979{
1980 if (!m_enableScriptExpressions)
1981 return false;
1982
1983 return true;
1984}
1985
1986void QQmlDomAstCreatorBase::endVisit(AST::FieldMemberExpression *expression)
1987{
1988 if (!m_enableScriptExpressions)
1989 return;
1990
1991 auto current = makeScriptElement<ScriptElements::BinaryExpression>(expression);
1993 current->addLocation(FileLocationRegion::OperatorTokenRegion, expression->dotToken);
1994
1995 if (expression->base) {
1997 current->setLeft(currentScriptNodeEl().takeVariant());
1998 removeCurrentScriptNode({});
1999 }
2000
2001 auto scriptIdentifier =
2002 std::make_shared<ScriptElements::IdentifierExpression>(expression->identifierToken);
2003 scriptIdentifier->setName(expression->name);
2004 current->setRight(ScriptElementVariant::fromElement(scriptIdentifier));
2005
2006 pushScriptElement(current);
2007}
2008
2009bool QQmlDomAstCreatorBase::visit(AST::ArrayMemberExpression *)
2010{
2011 if (!m_enableScriptExpressions)
2012 return false;
2013
2014 return true;
2015}
2016
2017void QQmlDomAstCreatorBase::endVisit(AST::ArrayMemberExpression *expression)
2018{
2019 if (!m_enableScriptExpressions)
2020 return;
2021
2022 auto current = makeScriptElement<ScriptElements::BinaryExpression>(expression);
2024 current->addLocation(FileLocationRegion::OperatorTokenRegion, expression->lbracketToken);
2025
2026 if (expression->expression) {
2028 // if scriptNodeStack.last() is fieldmember expression, add expression to it instead of
2029 // creating new one
2030 current->setRight(currentScriptNodeEl().takeVariant());
2031 removeCurrentScriptNode({});
2032 }
2033
2034 if (expression->base) {
2036 current->setLeft(currentScriptNodeEl().takeVariant());
2037 removeCurrentScriptNode({});
2038 }
2039
2040 pushScriptElement(current);
2041}
2042
2043bool QQmlDomAstCreatorBase::visit(AST::CallExpression *)
2044{
2045 if (!m_enableScriptExpressions)
2046 return false;
2047
2048 return true;
2049}
2050
2051void QQmlDomAstCreatorBase::endVisit(AST::CallExpression *exp)
2052{
2053 if (!m_enableScriptExpressions)
2054 return;
2055
2056 auto current = makeGenericScriptElement(exp, DomType::ScriptCallExpression);
2057 current->addLocation(LeftParenthesisRegion, exp->lparenToken);
2058 current->addLocation(RightParenthesisRegion, exp->rparenToken);
2059
2060 if (exp->arguments) {
2062 current->insertChild(Fields::arguments, currentScriptNodeEl().takeList());
2063 removeCurrentScriptNode({});
2064 } else {
2065 // insert empty list
2066 current->insertChild(Fields::arguments,
2067 ScriptElements::ScriptList(exp->lparenToken, exp->rparenToken));
2068 }
2069
2070 if (exp->base) {
2072 current->insertChild(Fields::callee, currentScriptNodeEl().takeVariant());
2073 removeCurrentScriptNode({});
2074 }
2075
2076 pushScriptElement(current);
2077}
2078
2079bool QQmlDomAstCreatorBase::visit(AST::ArrayPattern *)
2080{
2081 if (!m_enableScriptExpressions)
2082 return false;
2083
2084 return true;
2085}
2086
2087void QQmlDomAstCreatorBase::endVisit(AST::ArrayPattern *exp)
2088{
2089 if (!m_enableScriptExpressions)
2090 return;
2091
2092 auto current = makeGenericScriptElement(exp, DomType::ScriptArray);
2093
2094 if (exp->elements) {
2096 ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
2098 current->insertChild(Fields::elements, std::move(list));
2099
2100 removeCurrentScriptNode({});
2101 } else {
2102 // insert empty list
2103 current->insertChild(Fields::elements,
2104 ScriptElements::ScriptList(exp->lbracketToken, exp->rbracketToken));
2105 }
2106
2107 pushScriptElement(current);
2108}
2109
2110bool QQmlDomAstCreatorBase::visit(AST::ObjectPattern *)
2111{
2112 if (!m_enableScriptExpressions)
2113 return false;
2114
2115 return true;
2116}
2117
2118void QQmlDomAstCreatorBase::endVisit(AST::ObjectPattern *exp)
2119{
2120 if (!m_enableScriptExpressions)
2121 return;
2122
2123 auto current = makeGenericScriptElement(exp, DomType::ScriptObject);
2124
2125 if (exp->properties) {
2127 current->insertChild(Fields::properties, currentScriptNodeEl().takeList());
2128 removeCurrentScriptNode({});
2129 } else {
2130 // insert empty list
2131 current->insertChild(Fields::properties,
2132 ScriptElements::ScriptList(exp->lbraceToken, exp->rbraceToken));
2133 }
2134
2135 pushScriptElement(current);
2136}
2137
2138bool QQmlDomAstCreatorBase::visit(AST::PatternProperty *)
2139{
2140 if (!m_enableScriptExpressions)
2141 return false;
2142
2143 return true;
2144}
2145
2146void QQmlDomAstCreatorBase::endVisit(AST::PatternProperty *exp)
2147{
2148 if (!m_enableScriptExpressions)
2149 return;
2150
2151 auto current = makeGenericScriptElement(exp, DomType::ScriptProperty);
2152
2153 // handle the stuff from PatternProperty's base class PatternElement
2154 endVisitHelper(static_cast<PatternElement *>(exp), current);
2155
2156 // check if helper disabled scriptexpressions
2157 if (!m_enableScriptExpressions)
2158 return;
2159
2160 if (exp->name) {
2162 current->insertChild(Fields::name, currentScriptNodeEl().takeVariant());
2163 removeCurrentScriptNode({});
2164 }
2165
2166 pushScriptElement(current);
2167}
2168
2169bool QQmlDomAstCreatorBase::visit(AST::VariableStatement *)
2170{
2171 if (!m_enableScriptExpressions)
2172 return false;
2173
2174 return true;
2175}
2176
2177void QQmlDomAstCreatorBase::endVisit(AST::VariableStatement *statement)
2178{
2179 if (!m_enableScriptExpressions)
2180 return;
2181
2182 auto current = makeGenericScriptElement(statement, DomType::ScriptVariableDeclaration);
2183 current->addLocation(FileLocationRegion::TypeIdentifierRegion, statement->declarationKindToken);
2184
2185 if (statement->declarations) {
2187
2188 ScriptElements::ScriptList list = currentScriptNodeEl().takeList();
2191 current->insertChild(Fields::declarations, std::move(list));
2192
2193 removeCurrentScriptNode({});
2194 }
2195
2196 pushScriptElement(current);
2197}
2198
2199bool QQmlDomAstCreatorBase::visit(AST::Type *)
2200{
2201 if (!m_enableScriptExpressions)
2202 return false;
2203
2204 return true;
2205}
2206
2207void QQmlDomAstCreatorBase::endVisit(AST::Type *exp)
2208{
2209 if (!m_enableScriptExpressions)
2210 return;
2211
2212 auto current = makeGenericScriptElement(exp, DomType::ScriptType);
2213
2214 if (exp->typeArgument) {
2215 current->insertChild(Fields::typeArgumentName,
2216 fieldMemberExpressionForQualifiedId(exp->typeArgument));
2217 current->addLocation(FileLocationRegion::IdentifierRegion, combineLocations(exp->typeArgument));
2218 }
2219
2220 if (exp->typeId) {
2221 current->insertChild(Fields::typeName, fieldMemberExpressionForQualifiedId(exp->typeId));
2222 current->addLocation(FileLocationRegion::TypeIdentifierRegion, combineLocations(exp->typeId));
2223 }
2224
2225 pushScriptElement(current);
2226}
2227
2228bool QQmlDomAstCreatorBase::visit(AST::DefaultClause *)
2229{
2230 if (!m_enableScriptExpressions)
2231 return false;
2232
2233 return true;
2234}
2235
2236void QQmlDomAstCreatorBase::endVisit(AST::DefaultClause *exp)
2237{
2238 if (!m_enableScriptExpressions)
2239 return;
2240
2241 auto current = makeGenericScriptElement(exp, DomType::ScriptDefaultClause);
2242 current->addLocation(DefaultKeywordRegion, exp->defaultToken);
2243 current->addLocation(ColonTokenRegion, exp->colonToken);
2244
2245 if (exp->statements) {
2247 current->insertChild(Fields::statements, currentScriptNodeEl().takeList());
2248 removeCurrentScriptNode({});
2249 }
2250
2251 pushScriptElement(current);
2252}
2253
2254bool QQmlDomAstCreatorBase::visit(AST::CaseClause *)
2255{
2256 if (!m_enableScriptExpressions)
2257 return false;
2258
2259 return true;
2260}
2261
2262void QQmlDomAstCreatorBase::endVisit(AST::CaseClause *exp)
2263{
2264 if (!m_enableScriptExpressions)
2265 return;
2266
2267 auto current = makeGenericScriptElement(exp, DomType::ScriptCaseClause);
2268 current->addLocation(FileLocationRegion::CaseKeywordRegion, exp->caseToken);
2269 current->addLocation(FileLocationRegion::ColonTokenRegion, exp->colonToken);
2270
2271 if (exp->statements) {
2273 current->insertChild(Fields::statements, currentScriptNodeEl().takeList());
2274 removeCurrentScriptNode({});
2275 }
2276
2277 if (exp->expression) {
2279 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2280 removeCurrentScriptNode({});
2281 }
2282
2283 pushScriptElement(current);
2284}
2285
2286bool QQmlDomAstCreatorBase::visit(AST::CaseClauses *)
2287{
2288 if (!m_enableScriptExpressions)
2289 return false;
2290
2291 return true;
2292}
2293
2294void QQmlDomAstCreatorBase::endVisit(AST::CaseClauses *list)
2295{
2296 if (!m_enableScriptExpressions)
2297 return;
2298
2299 auto current = makeScriptList(list);
2300
2301 for (auto it = list; it; it = it->next) {
2303 current.append(scriptNodeStack.takeLast().takeVariant());
2304 }
2305
2306 current.reverse();
2307 pushScriptElement(current);
2308}
2309
2310bool QQmlDomAstCreatorBase::visit(AST::CaseBlock *)
2311{
2312 if (!m_enableScriptExpressions)
2313 return false;
2314
2315 return true;
2316}
2317
2318void QQmlDomAstCreatorBase::endVisit(AST::CaseBlock *exp)
2319{
2320 if (!m_enableScriptExpressions)
2321 return;
2322
2323 auto current = makeGenericScriptElement(exp, DomType::ScriptCaseBlock);
2324 current->addLocation(FileLocationRegion::LeftBraceRegion, exp->lbraceToken);
2325 current->addLocation(FileLocationRegion::RightBraceRegion, exp->rbraceToken);
2326
2327 if (exp->moreClauses) {
2329 current->insertChild(Fields::moreCaseClauses, currentScriptNodeEl().takeList());
2330 removeCurrentScriptNode({});
2331 }
2332
2333 if (exp->defaultClause) {
2335 current->insertChild(Fields::defaultClause, currentScriptNodeEl().takeVariant());
2336 removeCurrentScriptNode({});
2337 }
2338
2339 if (exp->clauses) {
2341 current->insertChild(Fields::caseClauses, currentScriptNodeEl().takeList());
2342 removeCurrentScriptNode({});
2343 }
2344 pushScriptElement(current);
2345}
2346
2347bool QQmlDomAstCreatorBase::visit(AST::SwitchStatement *)
2348{
2349 if (!m_enableScriptExpressions)
2350 return false;
2351
2352 return true;
2353}
2354
2355void QQmlDomAstCreatorBase::endVisit(AST::SwitchStatement *exp)
2356{
2357 if (!m_enableScriptExpressions)
2358 return;
2359
2360 auto current = makeGenericScriptElement(exp, DomType::ScriptSwitchStatement);
2361 current->addLocation(FileLocationRegion::SwitchKeywordRegion, exp->switchToken);
2362 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2363 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2364
2365 if (exp->block) {
2367 current->insertChild(Fields::caseBlock, currentScriptNodeEl().takeVariant());
2368 removeCurrentScriptNode({});
2369 }
2370 if (exp->expression) {
2372 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2373 removeCurrentScriptNode({});
2374 }
2375
2376 pushScriptElement(current);
2377}
2378
2379bool QQmlDomAstCreatorBase::visit(AST::WhileStatement *)
2380{
2381 if (!m_enableScriptExpressions)
2382 return false;
2383
2384 return true;
2385}
2386
2387void QQmlDomAstCreatorBase::endVisit(AST::WhileStatement *exp)
2388{
2389 if (!m_enableScriptExpressions)
2390 return;
2391
2392 auto current = makeGenericScriptElement(exp, DomType::ScriptWhileStatement);
2393 current->addLocation(FileLocationRegion::WhileKeywordRegion, exp->whileToken);
2394 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2395 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2396
2397 if (exp->statement) {
2399 current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
2400 removeCurrentScriptNode({});
2401 }
2402
2403 if (exp->expression) {
2405 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2406 removeCurrentScriptNode({});
2407 }
2408
2409 pushScriptElement(current);
2410}
2411
2412bool QQmlDomAstCreatorBase::visit(AST::DoWhileStatement *)
2413{
2414 if (!m_enableScriptExpressions)
2415 return false;
2416
2417 return true;
2418}
2419
2420void QQmlDomAstCreatorBase::endVisit(AST::DoWhileStatement *exp)
2421{
2422 if (!m_enableScriptExpressions)
2423 return;
2424
2425 auto current = makeGenericScriptElement(exp, DomType::ScriptDoWhileStatement);
2426 current->addLocation(FileLocationRegion::DoKeywordRegion, exp->doToken);
2427 current->addLocation(FileLocationRegion::WhileKeywordRegion, exp->whileToken);
2428 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2429 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2430
2431 if (exp->expression) {
2433 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2434 removeCurrentScriptNode({});
2435 }
2436
2437 if (exp->statement) {
2439 current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
2440 removeCurrentScriptNode({});
2441 }
2442
2443 pushScriptElement(current);
2444}
2445
2446bool QQmlDomAstCreatorBase::visit(AST::ForEachStatement *)
2447{
2448 if (!m_enableScriptExpressions)
2449 return false;
2450
2451 return true;
2452}
2453
2454void QQmlDomAstCreatorBase::endVisit(AST::ForEachStatement *exp)
2455{
2456 if (!m_enableScriptExpressions)
2457 return;
2458
2459 auto current = makeGenericScriptElement(exp, DomType::ScriptForEachStatement);
2460 current->addLocation(FileLocationRegion::ForKeywordRegion, exp->forToken);
2461 current->addLocation(FileLocationRegion::InOfTokenRegion, exp->inOfToken);
2462 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2463 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2464
2465 if (exp->statement) {
2467 current->insertChild(Fields::body, currentScriptNodeEl().takeVariant());
2468 removeCurrentScriptNode({});
2469 }
2470 if (exp->expression) {
2472 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2473 removeCurrentScriptNode({});
2474 }
2475
2476 if (exp->lhs) {
2478 current->insertChild(Fields::bindingElement, currentScriptNodeEl().takeVariant());
2479 removeCurrentScriptNode({});
2480
2481 if (auto pe = AST::cast<PatternElement *>(exp->lhs);
2482 pe && pe->declarationKindToken.isValid()) {
2483 current->addLocation(FileLocationRegion::TypeIdentifierRegion,
2484 pe->declarationKindToken);
2485 }
2486 }
2487
2488 pushScriptElement(current);
2489}
2490
2491
2492bool QQmlDomAstCreatorBase::visit(AST::ClassExpression *)
2493{
2494 // TODO: Add support for js expressions in classes
2495 // For now, turning off explicitly to avoid unwanted problems
2496 if (m_enableScriptExpressions)
2498 return true;
2499}
2500
2501void QQmlDomAstCreatorBase::endVisit(AST::ClassExpression *)
2502{
2503}
2504
2505void QQmlDomAstCreatorBase::endVisit(AST::TaggedTemplate *literal)
2506{
2507 if (!m_enableScriptExpressions)
2508 return;
2509 auto current = makeGenericScriptElement(literal, DomType::ScriptTaggedTemplate);
2511 current->insertChild(Fields::templateLiteral, scriptNodeStack.takeLast().takeVariant());
2513 current->insertChild(Fields::callee, scriptNodeStack.takeLast().takeVariant());
2514 pushScriptElement(current);
2515}
2516
2517/*!
2518\internal
2519Denotes the position of a template part in a template string. For example, in \c{`a${b}c${d}`}, \c a
2520is \c AtBeginning and \c{${d}} is \c AtEnd while the others are \c InMiddle, and in \c{`a`}, \c a is
2521\c AtBeginning and \c AtEnd.
2522 */
2523enum TemplatePartPosition : quint8 {
2524 InMiddle = 0,
2525 AtBeginning = 0x1,
2526 AtEnd = 0x2,
2527};
2528
2529Q_DECLARE_FLAGS(TemplatePartPositions, TemplatePartPosition)
2530
2531/*!
2532\internal
2533Sets the DollarLeftBraceTokenRegion sourcelocation in currentExpression if templatePartLocation
2534claims that toBeSplit ends in \c{${}.
2535*/
2536static void extractDollarBraceSourceLocationInto(
2537 const std::shared_ptr<ScriptElements::GenericScriptElement> &currentExpression,
2538 const SourceLocation &toBeSplit, QStringView code,
2539 TemplatePartPositions templatePartLocation)
2540{
2541 if (templatePartLocation & AtEnd || !currentExpression)
2542 return;
2543
2544 const auto offset = toBeSplit.offset + toBeSplit.length - 2;
2545 constexpr auto length = quint32(std::char_traits<char>::length("${"));
2546 const auto [row, column] = SourceLocation::rowAndColumnFrom(code, offset, toBeSplit);
2547 currentExpression->addLocation(FileLocationRegion::DollarLeftBraceTokenRegion,
2548 SourceLocation{ offset, length, row, column });
2549 return;
2550}
2551
2552/*!
2553\internal
2554See also \l extractDollarBraceSourceLocationInto.
2555*/
2556static void extractRightBacktickSourceLocationInto(
2557 const std::shared_ptr<ScriptElements::GenericScriptElement> &currentTemplate,
2558 const SourceLocation &toBeSplit, QStringView code,
2559 TemplatePartPositions templatePartLocation)
2560{
2561 if (!(templatePartLocation & AtEnd))
2562 return;
2563
2564 const auto offset = toBeSplit.offset + toBeSplit.length - 1;
2565 constexpr auto length = quint32(std::char_traits<char>::length("`"));
2566 const auto [row, column] = SourceLocation::rowAndColumnFrom(code, offset, toBeSplit);
2567 currentTemplate->addLocation(FileLocationRegion::RightBacktickTokenRegion,
2568 SourceLocation{ offset, length, row, column });
2569}
2570
2571/*!
2572\internal
2573See also \l extractDollarBraceSourceLocationInto.
2574*/
2575static void extractLeftBacktickSourceLocationInto(
2576 const std::shared_ptr<ScriptElements::GenericScriptElement> &currentTemplate,
2577 const SourceLocation &toBeSplit, TemplatePartPositions templatePartLocation)
2578{
2579 if (!(templatePartLocation & AtBeginning))
2580 return;
2581
2582 constexpr auto length = quint32(std::char_traits<char>::length("`"));
2583 const QQmlJS::SourceLocation leftBacktick{ toBeSplit.offset, length, toBeSplit.startLine,
2584 toBeSplit.startColumn };
2585 currentTemplate->addLocation(FileLocationRegion::LeftBacktickTokenRegion, leftBacktick);
2586}
2587
2588/*!
2589\internal
2590See also \l extractDollarBraceSourceLocationInto, but returns the extracted right brace instead of
2591inserting right away.
2592*/
2593static SourceLocation extractRightBraceSourceLocation(const SourceLocation &toBeSplit,
2594 TemplatePartPositions templatePartLocation)
2595{
2596 if (templatePartLocation & AtBeginning)
2597 return SourceLocation{};
2598
2599 // extract } at the beginning and insert in next loop iteration
2600 return SourceLocation{ toBeSplit.offset, 1, toBeSplit.startLine, toBeSplit.startColumn };
2601}
2602
2603/*!
2604\internal
2605Cleans the toBeSplit sourcelocation from potential backticks, dollar braces and right braces to only
2606contain the location of the string part.
2607*/
2608static SourceLocation extractStringLocation(const SourceLocation &toBeSplit, QStringView code,
2609 TemplatePartPositions location)
2610{
2611
2612 // remove "`" or "}" at beginning and "`" or "${" at the end of this location.
2613 const quint32 length = toBeSplit.length - (location & AtEnd ? 2 : 3);
2614 const quint32 offset = toBeSplit.offset + 1;
2615 const auto [row, column] = SourceLocation::rowAndColumnFrom(code, offset, toBeSplit);
2616 return SourceLocation{ offset, length, row, column };
2617}
2618
2619void QQmlDomAstCreatorBase::endVisit(AST::TemplateLiteral *literal)
2620{
2621 if (!m_enableScriptExpressions)
2622 return;
2623
2624 // AST::TemplateLiteral is a list and a TemplateLiteral at the same time:
2625 // in the Dom representation wrap the list into a separate TemplateLiteral Item.
2626 auto currentList = makeScriptList(literal);
2627 auto currentTemplate = makeGenericScriptElement(literal, DomType::ScriptTemplateLiteral);
2628
2629 const auto children = [&literal]() {
2630 std::vector<AST::TemplateLiteral *> result;
2631 for (auto it = literal; it; it = it->next) {
2632 result.push_back(it);
2633 }
2634 return result;
2635 }();
2636
2637 SourceLocation rightBrace;
2638 for (auto it = children.crbegin(); it != children.crend(); ++it) {
2639 // literalToken contains "`", "${", "}", for example "`asdf${" or "}asdf${"
2640 const QQmlJS::SourceLocation toBeSplit = (*it)->literalToken;
2641 std::shared_ptr<ScriptElements::GenericScriptElement> currentExpression;
2642
2643 if ((*it)->expression) {
2645 currentExpression = makeGenericScriptElement((*it)->expression,
2647
2648 currentExpression->insertChild(Fields::expression,
2649 scriptNodeStack.takeLast().takeVariant());
2650 if (rightBrace.isValid()) {
2651 currentExpression->addLocation(FileLocationRegion::RightBraceRegion,
2652 std::exchange(rightBrace, SourceLocation{}));
2653 }
2654 currentList.append(ScriptElementVariant::fromElement(currentExpression));
2655 }
2656
2657 if (!toBeSplit.isValid())
2658 continue;
2659
2660 const TemplatePartPositions location = [&it, &children]() {
2661 TemplatePartPositions result;
2662 if (it == children.crbegin())
2663 result |= AtEnd;
2664 if (it == std::prev(children.crend()))
2665 result |= AtBeginning;
2666 return result;
2667 }();
2668
2669 extractRightBacktickSourceLocationInto(currentTemplate, toBeSplit, qmlFilePtr->code(),
2670 location);
2671 extractLeftBacktickSourceLocationInto(currentTemplate, toBeSplit, location);
2672
2673 extractDollarBraceSourceLocationInto(currentExpression, toBeSplit, qmlFilePtr->code(),
2674 location);
2675 rightBrace = extractRightBraceSourceLocation(toBeSplit, location);
2676
2677 if ((*it)->rawValue.isEmpty())
2678 continue;
2679
2680 const SourceLocation stringLocation =
2681 extractStringLocation(toBeSplit, qmlFilePtr->code(), location);
2682 auto currentString =
2683 makeGenericScriptElement(stringLocation, DomType::ScriptTemplateStringPart);
2684 currentString->insertValue(Fields::value, (*it)->rawValue);
2685
2686 currentList.append(ScriptElementVariant::fromElement(currentString));
2687 }
2688 currentList.reverse();
2689
2690 currentTemplate->insertChild(Fields::components, currentList);
2691 pushScriptElement(currentTemplate);
2692}
2693
2694bool QQmlDomAstCreatorBase::visit(AST::TryStatement *)
2695{
2696 return m_enableScriptExpressions;
2697}
2698
2699void QQmlDomAstCreatorBase::endVisit(AST::TryStatement *statement)
2700{
2701 if (!m_enableScriptExpressions)
2702 return;
2703
2704 auto current = makeGenericScriptElement(statement, DomType::ScriptTryCatchStatement);
2705 current->addLocation(FileLocationRegion::TryKeywordRegion, statement->tryToken);
2706
2707 if (auto exp = statement->finallyExpression) {
2708 current->addLocation(FileLocationRegion::FinallyKeywordRegion, exp->finallyToken);
2709
2711 current->insertChild(Fields::finallyBlock, currentScriptNodeEl().takeVariant());
2712 removeCurrentScriptNode({});
2713 }
2714
2715 if (auto exp = statement->catchExpression) {
2716 current->addLocation(FileLocationRegion::CatchKeywordRegion, exp->catchToken);
2717 current->addLocation(FileLocationRegion::LeftParenthesisRegion, exp->lparenToken);
2718 current->addLocation(FileLocationRegion::RightParenthesisRegion, exp->rparenToken);
2719
2721 current->insertChild(Fields::catchBlock, currentScriptNodeEl().takeVariant());
2722 removeCurrentScriptNode({});
2724 current->insertChild(Fields::catchParameter, currentScriptNodeEl().takeVariant());
2725 removeCurrentScriptNode({});
2726 }
2727
2728 if (statement->statement) {
2730 current->insertChild(Fields::block, currentScriptNodeEl().takeVariant());
2731 removeCurrentScriptNode({});
2732 }
2733
2734 pushScriptElement(current);
2735}
2736
2737bool QQmlDomAstCreatorBase::visit(AST::Catch *)
2738{
2739 // handled in visit(AST::TryStatement* )
2740 return m_enableScriptExpressions;
2741}
2742
2743void QQmlDomAstCreatorBase::endVisit(AST::Catch *)
2744{
2745 // handled in endVisit(AST::TryStatement* )
2746}
2747
2748bool QQmlDomAstCreatorBase::visit(AST::Finally *)
2749{
2750 // handled in visit(AST::TryStatement* )
2751 return m_enableScriptExpressions;
2752}
2753
2754void QQmlDomAstCreatorBase::endVisit(AST::Finally *)
2755{
2756 // handled in endVisit(AST::TryStatement* )
2757}
2758
2759bool QQmlDomAstCreatorBase::visit(AST::ThrowStatement *)
2760{
2761 return m_enableScriptExpressions;
2762}
2763
2764void QQmlDomAstCreatorBase::endVisit(AST::ThrowStatement *statement)
2765{
2766 if (!m_enableScriptExpressions)
2767 return;
2768
2769 auto current = makeGenericScriptElement(statement, DomType::ScriptThrowStatement);
2770 current->addLocation(FileLocationRegion::ThrowKeywordRegion, statement->throwToken);
2771
2772 if (statement->expression) {
2774 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2775 removeCurrentScriptNode({});
2776 }
2777
2778 pushScriptElement(current);
2779}
2780
2781bool QQmlDomAstCreatorBase::visit(AST::LabelledStatement *)
2782{
2783 return m_enableScriptExpressions;
2784}
2785
2786void QQmlDomAstCreatorBase::endVisit(AST::LabelledStatement *statement)
2787{
2788 if (!m_enableScriptExpressions)
2789 return;
2790
2791 auto current = makeGenericScriptElement(statement, DomType::ScriptLabelledStatement);
2792 current->addLocation(FileLocationRegion::ColonTokenRegion, statement->colonToken);
2793
2794 auto label = std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
2795 label->setName(statement->label);
2796 current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
2797
2798
2799 if (statement->statement) {
2801 current->insertChild(Fields::statement, currentScriptNodeEl().takeVariant());
2802 removeCurrentScriptNode({});
2803 }
2804
2805 pushScriptElement(current);
2806}
2807
2808bool QQmlDomAstCreatorBase::visit(AST::BreakStatement *)
2809{
2810 return m_enableScriptExpressions;
2811}
2812
2813void QQmlDomAstCreatorBase::endVisit(AST::BreakStatement *statement)
2814{
2815 if (!m_enableScriptExpressions)
2816 return;
2817
2818 auto current = makeGenericScriptElement(statement, DomType::ScriptBreakStatement);
2819 current->addLocation(FileLocationRegion::BreakKeywordRegion, statement->breakToken);
2820
2821 if (!statement->label.isEmpty()) {
2822 auto label =
2823 std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
2824 label->setName(statement->label);
2825 current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
2826 }
2827
2828 pushScriptElement(current);
2829}
2830
2831bool QQmlDomAstCreatorBase::visit(AST::CommaExpression *)
2832{
2833 return m_enableScriptExpressions;
2834}
2835
2836void QQmlDomAstCreatorBase::endVisit(AST::CommaExpression *commaExpression)
2837{
2838 if (!m_enableScriptExpressions)
2839 return;
2840
2841 auto current = makeScriptElement<ScriptElements::BinaryExpression>(commaExpression);
2842 current->addLocation(OperatorTokenRegion, commaExpression->commaToken);
2843
2844 if (commaExpression->right) {
2846 current->setRight(currentScriptNodeEl().takeVariant());
2847 removeCurrentScriptNode({});
2848 }
2849
2850 if (commaExpression->left) {
2852 current->setLeft(currentScriptNodeEl().takeVariant());
2853 removeCurrentScriptNode({});
2854 }
2855
2856 pushScriptElement(current);
2857}
2858
2859bool QQmlDomAstCreatorBase::visit(AST::ConditionalExpression *)
2860{
2861 return m_enableScriptExpressions;
2862}
2863
2864void QQmlDomAstCreatorBase::endVisit(AST::ConditionalExpression *expression)
2865{
2866 if (!m_enableScriptExpressions)
2867 return;
2868
2869 auto current = makeGenericScriptElement(expression, DomType::ScriptConditionalExpression);
2870 current->addLocation(FileLocationRegion::QuestionMarkTokenRegion, expression->questionToken);
2871 current->addLocation(FileLocationRegion::ColonTokenRegion, expression->colonToken);
2872
2873 if (expression->ko) {
2875 current->insertChild(Fields::alternative, currentScriptNodeEl().takeVariant());
2876 removeCurrentScriptNode({});
2877 }
2878
2879 if (expression->ok) {
2881 current->insertChild(Fields::consequence, currentScriptNodeEl().takeVariant());
2882 removeCurrentScriptNode({});
2883 }
2884
2885 if (expression->expression) {
2887 current->insertChild(Fields::condition, currentScriptNodeEl().takeVariant());
2888 removeCurrentScriptNode({});
2889 }
2890
2891 pushScriptElement(current);
2892}
2893
2894bool QQmlDomAstCreatorBase::visit(AST::ContinueStatement *)
2895{
2896 return m_enableScriptExpressions;
2897}
2898
2899void QQmlDomAstCreatorBase::endVisit(AST::ContinueStatement *statement)
2900{
2901 if (!m_enableScriptExpressions)
2902 return;
2903
2904 auto current = makeGenericScriptElement(statement, DomType::ScriptContinueStatement);
2905 current->addLocation(FileLocationRegion::ContinueKeywordRegion, statement->continueToken);
2906
2907 if (!statement->label.isEmpty()) {
2908 auto label =
2909 std::make_shared<ScriptElements::IdentifierExpression>(statement->identifierToken);
2910 label->setName(statement->label);
2911 current->insertChild(Fields::label, ScriptElementVariant::fromElement(label));
2912 }
2913
2914 pushScriptElement(current);
2915}
2916
2917/*!
2918 \internal
2919 Helper to create unary expressions from AST nodes.
2920 \sa makeGenericScriptElement
2921 */
2923QQmlDomAstCreatorBase::makeUnaryExpression(AST::Node *expression, QQmlJS::SourceLocation operatorToken,
2924 bool hasExpression, UnaryExpressionKind kind)
2925{
2926 const DomType type = [&kind]() {
2927 switch (kind) {
2928 case Prefix:
2930 case Postfix:
2932 }
2933 Q_UNREACHABLE_RETURN(DomType::ScriptUnaryExpression);
2934 }();
2935
2936 auto current = makeGenericScriptElement(expression, type);
2937 current->addLocation(FileLocationRegion::OperatorTokenRegion, operatorToken);
2938
2939 if (hasExpression) {
2942 return {};
2943 }
2944 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
2945 removeCurrentScriptNode({});
2946 }
2947
2948 return current;
2949}
2950
2951bool QQmlDomAstCreatorBase::visit(AST::UnaryMinusExpression *)
2952{
2953 return m_enableScriptExpressions;
2954}
2955
2956void QQmlDomAstCreatorBase::endVisit(AST::UnaryMinusExpression *statement)
2957{
2958 if (!m_enableScriptExpressions)
2959 return;
2960
2961 auto current =
2962 makeUnaryExpression(statement, statement->minusToken, statement->expression, Prefix);
2963 if (!current)
2964 return;
2965
2966 pushScriptElement(current);
2967}
2968
2969bool QQmlDomAstCreatorBase::visit(AST::UnaryPlusExpression *)
2970{
2971 return m_enableScriptExpressions;
2972}
2973
2974void QQmlDomAstCreatorBase::endVisit(AST::UnaryPlusExpression *statement)
2975{
2976 if (!m_enableScriptExpressions)
2977 return;
2978
2979 auto current =
2980 makeUnaryExpression(statement, statement->plusToken, statement->expression, Prefix);
2981 if (!current)
2982 return;
2983
2984 pushScriptElement(current);
2985}
2986
2987bool QQmlDomAstCreatorBase::visit(AST::TildeExpression *)
2988{
2989 return m_enableScriptExpressions;
2990}
2991
2992void QQmlDomAstCreatorBase::endVisit(AST::TildeExpression *statement)
2993{
2994 if (!m_enableScriptExpressions)
2995 return;
2996
2997 auto current =
2998 makeUnaryExpression(statement, statement->tildeToken, statement->expression, Prefix);
2999 if (!current)
3000 return;
3001
3002 pushScriptElement(current);
3003}
3004
3005bool QQmlDomAstCreatorBase::visit(AST::NotExpression *)
3006{
3007 return m_enableScriptExpressions;
3008}
3009
3010void QQmlDomAstCreatorBase::endVisit(AST::NotExpression *statement)
3011{
3012 if (!m_enableScriptExpressions)
3013 return;
3014
3015 auto current =
3016 makeUnaryExpression(statement, statement->notToken, statement->expression, Prefix);
3017 if (!current)
3018 return;
3019
3020 pushScriptElement(current);
3021}
3022
3023bool QQmlDomAstCreatorBase::visit(AST::TypeOfExpression *)
3024{
3025 return m_enableScriptExpressions;
3026}
3027
3028void QQmlDomAstCreatorBase::endVisit(AST::TypeOfExpression *statement)
3029{
3030 if (!m_enableScriptExpressions)
3031 return;
3032
3033 auto current =
3034 makeUnaryExpression(statement, statement->typeofToken, statement->expression, Prefix);
3035 if (!current)
3036 return;
3037
3038 pushScriptElement(current);
3039}
3040
3041bool QQmlDomAstCreatorBase::visit(AST::DeleteExpression *)
3042{
3043 return m_enableScriptExpressions;
3044}
3045
3046void QQmlDomAstCreatorBase::endVisit(AST::DeleteExpression *statement)
3047{
3048 if (!m_enableScriptExpressions)
3049 return;
3050
3051 auto current =
3052 makeUnaryExpression(statement, statement->deleteToken, statement->expression, Prefix);
3053 if (!current)
3054 return;
3055
3056 pushScriptElement(current);
3057}
3058
3059bool QQmlDomAstCreatorBase::visit(AST::VoidExpression *)
3060{
3061 return m_enableScriptExpressions;
3062}
3063
3064void QQmlDomAstCreatorBase::endVisit(AST::VoidExpression *statement)
3065{
3066 if (!m_enableScriptExpressions)
3067 return;
3068
3069 auto current =
3070 makeUnaryExpression(statement, statement->voidToken, statement->expression, Prefix);
3071 if (!current)
3072 return;
3073
3074 pushScriptElement(current);
3075}
3076
3077bool QQmlDomAstCreatorBase::visit(AST::PostDecrementExpression *)
3078{
3079 return m_enableScriptExpressions;
3080}
3081
3082void QQmlDomAstCreatorBase::endVisit(AST::PostDecrementExpression *statement)
3083{
3084 if (!m_enableScriptExpressions)
3085 return;
3086
3087 auto current =
3088 makeUnaryExpression(statement, statement->decrementToken, statement->base, Postfix);
3089 if (!current)
3090 return;
3091
3092 pushScriptElement(current);
3093}
3094
3095bool QQmlDomAstCreatorBase::visit(AST::PostIncrementExpression *)
3096{
3097 return m_enableScriptExpressions;
3098}
3099
3100void QQmlDomAstCreatorBase::endVisit(AST::PostIncrementExpression *statement)
3101{
3102 if (!m_enableScriptExpressions)
3103 return;
3104
3105 auto current =
3106 makeUnaryExpression(statement, statement->incrementToken, statement->base, Postfix);
3107 if (!current)
3108 return;
3109
3110 pushScriptElement(current);
3111}
3112
3113bool QQmlDomAstCreatorBase::visit(AST::PreIncrementExpression *)
3114{
3115 return m_enableScriptExpressions;
3116}
3117
3118void QQmlDomAstCreatorBase::endVisit(AST::PreIncrementExpression *statement)
3119{
3120 if (!m_enableScriptExpressions)
3121 return;
3122
3123 auto current = makeUnaryExpression(statement, statement->incrementToken, statement->expression,
3124 Prefix);
3125 if (!current)
3126 return;
3127
3128 pushScriptElement(current);
3129}
3130
3131bool QQmlDomAstCreatorBase::visit(AST::EmptyStatement *)
3132{
3133 return m_enableScriptExpressions;
3134}
3135
3136void QQmlDomAstCreatorBase::endVisit(AST::EmptyStatement *statement)
3137{
3138 if (!m_enableScriptExpressions)
3139 return;
3140
3141 auto current = makeGenericScriptElement(statement, DomType::ScriptEmptyStatement);
3142 current->addLocation(FileLocationRegion::SemicolonTokenRegion, statement->semicolonToken);
3143 pushScriptElement(current);
3144}
3145
3146bool QQmlDomAstCreatorBase::visit(AST::NestedExpression *)
3147{
3148 return m_enableScriptExpressions;
3149}
3150
3151void QQmlDomAstCreatorBase::endVisit(AST::NestedExpression *expression)
3152{
3153 if (!m_enableScriptExpressions)
3154 return;
3155
3156 auto current = makeGenericScriptElement(expression, DomType::ScriptParenthesizedExpression);
3157 current->addLocation(FileLocationRegion::LeftParenthesisRegion, expression->lparenToken);
3158 current->addLocation(FileLocationRegion::RightParenthesisRegion, expression->rparenToken);
3159
3160 if (expression->expression) {
3162 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
3163 removeCurrentScriptNode({});
3164 }
3165
3166 pushScriptElement(current);
3167}
3168
3169bool QQmlDomAstCreatorBase::visit(AST::NewExpression *)
3170{
3171 return m_enableScriptExpressions;
3172}
3173
3174void QQmlDomAstCreatorBase::endVisit(AST::NewExpression *expression)
3175{
3176 if (!m_enableScriptExpressions)
3177 return;
3178
3179 auto current = makeGenericScriptElement(expression, DomType::ScriptNewExpression);
3180 current->addLocation(FileLocationRegion::NewKeywordRegion, expression->newToken);
3181
3182 if (expression->expression) {
3184 current->insertChild(Fields::expression, currentScriptNodeEl().takeVariant());
3185 removeCurrentScriptNode({});
3186 }
3187
3188 pushScriptElement(current);
3189}
3190
3191bool QQmlDomAstCreatorBase::visit(AST::NewMemberExpression *)
3192{
3193 return m_enableScriptExpressions;
3194}
3195
3196void QQmlDomAstCreatorBase::endVisit(AST::NewMemberExpression *expression)
3197{
3198 if (!m_enableScriptExpressions)
3199 return;
3200
3201 auto current = makeGenericScriptElement(expression, DomType::ScriptNewMemberExpression);
3202 current->addLocation(FileLocationRegion::NewKeywordRegion, expression->newToken);
3203 current->addLocation(FileLocationRegion::LeftParenthesisRegion, expression->lparenToken);
3204 current->addLocation(FileLocationRegion::RightParenthesisRegion, expression->rparenToken);
3205
3206 if (expression->arguments) {
3208 current->insertChild(Fields::arguments, scriptNodeStack.takeLast().takeList());
3209 }
3210 if (expression->base) {
3212 current->insertChild(Fields::base, scriptNodeStack.takeLast().takeVariant());
3213 }
3214
3215 pushScriptElement(current);
3216}
3217
3218void QQmlDomAstCreatorBase::endVisit(AST::WithStatement *ast)
3219{
3220 if (!m_enableScriptExpressions)
3221 return;
3222
3223 auto current = makeGenericScriptElement(ast, DomType::ScriptWithStatement);
3224
3225 if (ast->statement) {
3227 current->insertChild(Fields::statement, scriptNodeStack.takeLast().takeVariant());
3228 }
3229 if (ast->expression) {
3231 current->insertChild(Fields::expression, scriptNodeStack.takeLast().takeVariant());
3232 }
3233
3234 pushScriptElement(current);
3235}
3236
3237bool QQmlDomAstCreatorBase::visit(AST::PreDecrementExpression *)
3238{
3239 return m_enableScriptExpressions;
3240}
3241
3242void QQmlDomAstCreatorBase::endVisit(AST::PreDecrementExpression *statement)
3243{
3244 if (!m_enableScriptExpressions)
3245 return;
3246
3247 auto current = makeUnaryExpression(statement, statement->decrementToken, statement->expression,
3248 Prefix);
3249 if (!current)
3250 return;
3251
3252 pushScriptElement(current);
3253}
3254
3255static const DomEnvironment *environmentFrom(MutableDomItem &qmlFile)
3256{
3257 auto top = qmlFile.top();
3258 if (!top) {
3259 return {};
3260 }
3261 auto domEnvironment = top.as<DomEnvironment>();
3262 if (!domEnvironment) {
3263 return {};
3264 }
3265 return domEnvironment;
3266}
3267
3268static QStringList qmldirFilesFrom(MutableDomItem &qmlFile)
3269{
3270 if (auto env = environmentFrom(qmlFile))
3271 return env->qmldirFiles();
3272
3273 return {};
3274}
3275
3277 QQmlJSLogger *logger,
3278 QQmlJSImporter *importer)
3279 : m_logger(logger),
3285{
3286}
3287
3288#define X(name)
3289 bool QQmlDomAstCreatorWithQQmlJSScope::visit(name *node)
3290 {
3291 return visitT(node);
3292 }
3293 void QQmlDomAstCreatorWithQQmlJSScope::endVisit(name *node)
3294 {
3295 endVisitT(node);
3296 }
3297QQmlJSASTClassListToVisit
3298#undef X
3299
3300void QQmlDomAstCreatorWithQQmlJSScope::setScopeInDomAfterEndvisit()
3301{
3302 const QQmlJSScope::ConstPtr scope = m_scopeCreator.m_currentScope;
3303 if (!m_domCreator.scriptNodeStack.isEmpty()) {
3304 auto topOfStack = m_domCreator.currentScriptNodeEl();
3305 switch (topOfStack.kind) {
3307 // A ScriptIdentifierExpression in a QML scope is an actual ID.
3308 if (scope->scopeType() == QQmlJSScope::ScopeType::QMLScope)
3309 m_domCreator.currentScriptNodeEl().setSemanticScope(scope);
3310 break;
3311 }
3312 case DomType::ScriptBlockStatement:
3313 case DomType::ScriptForStatement:
3314 case DomType::ScriptForEachStatement:
3315 case DomType::ScriptDoWhileStatement:
3316 case DomType::ScriptWhileStatement:
3317 case DomType::List:
3318 m_domCreator.currentScriptNodeEl().setSemanticScope(scope);
3319 break;
3321 // Put the body's scope into the function expression: function expressions will contain
3322 // their parents scope instead of their own without this
3323 auto element = m_domCreator.currentScriptNodeEl().value;
3324 auto scriptElementVariant = std::get_if<ScriptElementVariant>(&element);
3325 if (!scriptElementVariant || !scriptElementVariant->data())
3326 break;
3327 scriptElementVariant->visit([](auto &&e) {
3328 using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
3329 if (e->kind() != DomType::ScriptFunctionExpression)
3330 return;
3331
3332 if constexpr (std::is_same_v<U,
3333 ScriptElement::PointerType<
3334 ScriptElements::GenericScriptElement>>) {
3335 if (auto bodyPtr = e->elementChild(Fields::body)) {
3336 const auto bodyScope = bodyPtr.base()->semanticScope();
3337 e->setSemanticScope(bodyScope);
3338 }
3339 }
3340 });
3341 break;
3342 }
3343
3344 // TODO: find which script elements also have a scope and implement them here
3345 default:
3346 break;
3347 };
3348 } else if (!m_domCreator.nodeStack.isEmpty()) {
3349 std::visit(
3350 [&scope](auto &&e) {
3351 using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
3352 // TODO: find which dom elements also have a scope and implement them here
3353 if constexpr (std::is_same_v<U, QmlObject>) {
3354 e.setSemanticScope(scope);
3355 } else if constexpr (std::is_same_v<U, QmlComponent>) {
3356 e.setSemanticScope(scope);
3357 } else if constexpr (std::is_same_v<U, MethodInfo>) {
3358 if (e.body) {
3359 if (auto scriptElement = e.body->scriptElement()) {
3360 scriptElement.base()->setSemanticScope(scope);
3361 }
3362 }
3363 e.setSemanticScope(scope);
3364 }
3365 },
3366 m_domCreator.currentNodeEl().item.value);
3367 }
3368}
3369
3370void QQmlDomAstCreatorWithQQmlJSScope::setScopeInDomBeforeEndvisit()
3371{
3372 const QQmlJSScope::ConstPtr scope = m_scopeCreator.m_currentScope;
3373
3374 // depending whether the property definition has a binding, the property definition might be
3375 // either at the last position in the stack or at the position before the last position.
3376 if (m_domCreator.nodeStack.size() > 1
3377 && m_domCreator.nodeStack.last().item.kind == DomType::Binding) {
3378 std::visit(
3379 [&scope](auto &&e) {
3380 using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
3381 if constexpr (std::is_same_v<U, PropertyDefinition>) {
3382 // Make sure to use the property definition scope instead of the binding
3383 // scope. If the current scope is a binding scope (this happens when the
3384 // property definition has a binding, like `property int i: 45` for
3385 // example), then the property definition scope is the parent of the current
3386 // scope.
3387 const bool usePropertyDefinitionScopeInsteadOfTheBindingScope =
3388 QQmlSA::isFunctionScope(scope->scopeType())
3389 && scope->parentScope()
3390 && scope->parentScope()->scopeType() == QQmlSA::ScopeType::QMLScope;
3391 e.setSemanticScope(usePropertyDefinitionScopeInsteadOfTheBindingScope
3392 ? scope->parentScope()
3393 : scope);
3394 }
3395 },
3396 m_domCreator.currentNodeEl(1).item.value);
3397 }
3398 if (m_domCreator.nodeStack.size() > 0) {
3399 std::visit(
3400 [&scope](auto &&e) {
3401 using U = std::remove_cv_t<std::remove_reference_t<decltype(e)>>;
3402 if constexpr (std::is_same_v<U, PropertyDefinition>) {
3403 e.setSemanticScope(scope);
3404 Q_ASSERT(e.semanticScope());
3405 } else if constexpr (std::is_same_v<U, MethodInfo>) {
3406 if (e.methodType == MethodInfo::Signal) {
3407 e.setSemanticScope(scope);
3408 }
3409 }
3410 },
3411 m_domCreator.currentNodeEl().item.value);
3412 }
3413}
3414
3416{
3417}
3418
3419#define X(name)
3420 bool QQmlDomAstCreator::visit(AST::name *ast)
3421 {
3422 return QQmlDomAstCreatorBase::visit(ast) && visitWithCustomListIteration(ast, this);
3423 }
3425#undef X
3426
3427} // end namespace Dom
3428} // end namespace QQmlJS
3429
3430#undef Q_SCRIPTELEMENT_DISABLE
3431#undef Q_SCRIPTELEMENT_EXIT_IF
3432
3433QT_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)