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