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