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