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