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

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