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

source code of qtdeclarative/src/qmldom/qqmldomastcreator.cpp