Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmljsimportvisitor.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5#include "qqmljslogger_p.h"
6#include "qqmljsmetatypes_p.h"
8
9#include <QtCore/qfileinfo.h>
10#include <QtCore/qdir.h>
11#include <QtCore/qqueue.h>
12#include <QtCore/qscopedvaluerollback.h>
13#include <QtCore/qpoint.h>
14#include <QtCore/qrect.h>
15#include <QtCore/qsize.h>
16
17#include <QtQml/private/qqmlsignalnames_p.h>
18#include <QtQml/private/qv4codegen_p.h>
19#include <QtQml/private/qqmlstringconverters_p.h>
20#include <QtQml/private/qqmlirbuilder_p.h>
21#include "qqmljsscope_p.h"
22#include "qqmljsutils_p.h"
23#include "qqmljsloggingutils.h"
24#include "qqmlsaconstants.h"
25
26#include <algorithm>
27#include <limits>
28#include <optional>
29#include <variant>
30
32
33using namespace Qt::StringLiterals;
34
35using namespace QQmlJS::AST;
36
43 const QQmlJSScope::ConstPtr &assignedType)
44{
45 // See QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents()
46 // for the logic in qqmltypecompiler
47
48 // Note: unlike findAndRegisterImplicitComponents() we do not check whether
49 // the property type is *derived* from QQmlComponent at some point because
50 // this is actually meaningless (and in the case of QQmlComponent::create()
51 // gets rejected in QQmlPropertyValidator): if the type is not a
52 // QQmlComponent, we have a type mismatch because of assigning a Component
53 // object to a non-Component property
54 const bool propertyVerdict = property.type()->internalName() == u"QQmlComponent";
55
56 const bool assignedTypeVerdict = [&assignedType]() {
57 // Note: nonCompositeBaseType covers the case when assignedType itself
58 // is non-composite
59 auto cppBase = QQmlJSScope::nonCompositeBaseType(assignedType);
60 Q_ASSERT(cppBase); // any QML type has (or must have) a C++ base type
61
62 // See isUsableComponent() in qqmltypecompiler.cpp: along with checking
63 // whether a type has a QQmlComponent static meta object (which we
64 // substitute here with checking the first non-composite base for being
65 // a QQmlComponent), it also excludes QQmlAbstractDelegateComponent
66 // subclasses from implicit wrapping
67 if (cppBase->internalName() == u"QQmlComponent")
68 return false;
69 for (; cppBase; cppBase = cppBase->baseType()) {
70 if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
71 return false;
72 }
73 return true;
74 }();
75
76 return propertyVerdict && assignedTypeVerdict;
77}
78
92
106
107template<typename Node>
109{
111 for (const Node *segment = node; segment; segment = segment->next) {
112 if (!result.isEmpty())
113 result += u'.';
114 result += segment->name;
115 }
116 return result;
117}
118
120 const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger,
121 const QString &implicitImportDirectory, const QStringList &qmldirFiles)
122 : m_implicitImportDirectory(implicitImportDirectory),
123 m_qmldirFiles(qmldirFiles),
124 m_currentScope(QQmlJSScope::create()),
125 m_exportedRootScope(target),
126 m_importer(importer),
127 m_logger(logger),
128 m_rootScopeImports(
129 QQmlJSImporter::ImportedTypes::QML, {},
130 importer->builtinInternalNames().arrayType())
131{
132 m_currentScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope);
133 Q_ASSERT(logger); // must be valid
134
135 m_globalScope = m_currentScope;
136 m_currentScope->setIsComposite(true);
137
138 m_currentScope->setInternalName(u"global"_s);
139
140 QLatin1String jsGlobVars[] = { /* Not listed on the MDN page; browser and QML extensions: */
141 // console/debug api
142 QLatin1String("console"), QLatin1String("print"),
143 // garbage collector
144 QLatin1String("gc"),
145 // i18n
146 QLatin1String("qsTr"), QLatin1String("qsTrId"),
147 QLatin1String("QT_TR_NOOP"), QLatin1String("QT_TRANSLATE_NOOP"),
148 QLatin1String("QT_TRID_NOOP"),
149 // XMLHttpRequest
150 QLatin1String("XMLHttpRequest")
151 };
152
153 QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
155 true
156 };
157 for (const char **globalName = QV4::Compiler::Codegen::s_globalNames; *globalName != nullptr;
158 ++globalName) {
159 m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName), globalJavaScript);
160 }
161 for (const auto &jsGlobVar : jsGlobVars)
162 m_currentScope->insertJSIdentifier(jsGlobVar, globalJavaScript);
163}
164
166
167void QQmlJSImportVisitor::populateCurrentScope(
169{
175 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope);
176}
177
178void QQmlJSImportVisitor::enterRootScope(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location)
179{
182 populateCurrentScope(type, name, location);
183}
184
187{
190 m_currentScope = std::move(newScope);
191 populateCurrentScope(type, name, location);
192}
193
195 const QString &name,
197{
200
201 const auto pred = [&](const QQmlJSScope::ConstPtr &s) {
202 // it's either attached or group property, so use internalName()
203 // directly. see setScopeName() for details
204 return s->internalName() == name;
205 };
206 const auto scopes = m_currentScope->childScopes();
207 // TODO: linear search. might want to make childScopes() a set/hash-set and
208 // use faster algorithm here
209 auto it = std::find_if(scopes.begin(), scopes.end(), pred);
210 if (it == scopes.end()) {
211 // create and enter new scope
213 return false;
214 }
215 // enter found scope
216 m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, *it);
218 return true;
219}
220
225
227{
228 const auto handleUnresolvedType = [this](const QQmlJSScope::ConstPtr &type) {
229 m_logger->log(QStringLiteral("Type %1 is used but it is not resolved")
230 .arg(getScopeName(type, type->scopeType())),
231 qmlUnresolvedType, type->sourceLocation());
232 };
233 return isTypeResolved(type, handleUnresolvedType);
234}
235
240
241void QQmlJSImportVisitor::resolveAliasesAndIds()
242{
243 QQueue<QQmlJSScope::Ptr> objects;
244 objects.enqueue(m_exportedRootScope);
245
246 qsizetype lastRequeueLength = std::numeric_limits<qsizetype>::max();
247 QQueue<QQmlJSScope::Ptr> requeue;
248
249 while (!objects.isEmpty()) {
250 const QQmlJSScope::Ptr object = objects.dequeue();
251 const auto properties = object->ownProperties();
252
253 bool doRequeue = false;
254 for (const auto &property : properties) {
255 if (!property.isAlias() || !property.type().isNull())
256 continue;
257
258 QStringList components = property.aliasExpression().split(u'.');
259 QQmlJSMetaProperty targetProperty;
260
261 bool foundProperty = false;
262
263 // The first component has to be an ID. Find the object it refers to.
265 QQmlJSScope::ConstPtr typeScope;
266 if (!type.isNull()) {
267 foundProperty = true;
268
269 // Any further components are nested properties of that object.
270 // Technically we can only resolve a limited depth in the engine, but the rules
271 // on that are fuzzy and subject to change. Let's ignore it for now.
272 // If the target is itself an alias and has not been resolved, re-queue the object
273 // and try again later.
274 while (type && !components.isEmpty()) {
275 const QString name = components.takeFirst();
276
277 if (!type->hasProperty(name)) {
278 foundProperty = false;
279 type = {};
280 break;
281 }
282
283 const auto target = type->property(name);
284 if (!target.type() && target.isAlias())
285 doRequeue = true;
286 typeScope = type;
287 type = target.type();
288 targetProperty = target;
289 }
290 }
291
292 if (type.isNull()) {
293 if (doRequeue)
294 continue;
295 if (foundProperty) {
296 m_logger->log(QStringLiteral("Cannot deduce type of alias \"%1\"")
297 .arg(property.propertyName()),
298 qmlMissingType, object->sourceLocation());
299 } else {
300 m_logger->log(QStringLiteral("Cannot resolve alias \"%1\"")
301 .arg(property.propertyName()),
302 qmlUnresolvedAlias, object->sourceLocation());
303 }
304
305 Q_ASSERT(property.index() >= 0); // this property is already in object
306 object->addOwnProperty(property);
307
308 } else {
309 QQmlJSMetaProperty newProperty = property;
310 newProperty.setType(type);
311 // Copy additional property information from target
312 newProperty.setIsList(targetProperty.isList());
313 newProperty.setIsWritable(targetProperty.isWritable());
314 newProperty.setIsPointer(targetProperty.isPointer());
315
316 if (!typeScope.isNull() && !object->isPropertyLocallyRequired(property.propertyName())) {
317 object->setPropertyLocallyRequired(
318 newProperty.propertyName(),
319 typeScope->isPropertyRequired(targetProperty.propertyName()));
320 }
321
322 if (const QString internalName = type->internalName(); !internalName.isEmpty())
323 newProperty.setTypeName(internalName);
324
325 Q_ASSERT(newProperty.index() >= 0); // this property is already in object
326 object->addOwnProperty(newProperty);
327 }
328 }
329
330 const auto childScopes = object->childScopes();
331 for (const auto &childScope : childScopes) {
333 const QString name = childScope->internalName();
334 if (object->isNameDeferred(name)) {
335 const QQmlJSScope::ConstPtr deferred = m_scopesById.scope(name, childScope);
336 if (!deferred.isNull()) {
338 childScope, deferred, m_rootScopeImports, &m_usedTypes);
339 }
340 }
341 }
342 objects.enqueue(childScope);
343 }
344
345 if (doRequeue)
346 requeue.enqueue(object);
347
348 if (objects.isEmpty() && requeue.size() < lastRequeueLength) {
349 lastRequeueLength = requeue.size();
350 objects.swap(requeue);
351 }
352 }
353
354 while (!requeue.isEmpty()) {
355 const QQmlJSScope::Ptr object = requeue.dequeue();
356 const auto properties = object->ownProperties();
357 for (const auto &property : properties) {
358 if (!property.isAlias() || property.type())
359 continue;
360 m_logger->log(QStringLiteral("Alias \"%1\" is part of an alias cycle")
361 .arg(property.propertyName()),
362 qmlAliasCycle, object->sourceLocation());
363 }
364 }
365}
366
368 const QString &localFile, QQmlJSResourceFileMapper *mapper)
369{
370 if (mapper) {
371 const auto resource = mapper->entry(
373 if (resource.isValid()) {
374 return resource.resourcePath.contains(u'/')
375 ? (u':' + resource.resourcePath.left(
376 resource.resourcePath.lastIndexOf(u'/') + 1))
377 : QStringLiteral(":/");
378 }
379 }
380
381 return QFileInfo(localFile).canonicalPath() + u'/';
382}
383
384void QQmlJSImportVisitor::processImportWarnings(
385 const QString &what, const QQmlJS::SourceLocation &srcLocation)
386{
387 const auto warnings = m_importer->takeWarnings();
388 if (warnings.isEmpty())
389 return;
390
391 m_logger->log(QStringLiteral("Warnings occurred while importing %1:").arg(what), qmlImport,
392 srcLocation);
394}
395
396void QQmlJSImportVisitor::importBaseModules()
397{
398 Q_ASSERT(m_rootScopeImports.types().isEmpty());
400
401 const QQmlJS::SourceLocation invalidLoc;
402 for (auto it = m_rootScopeImports.types().keyBegin(), end = m_rootScopeImports.types().keyEnd();
403 it != end; it++) {
404 addImportWithLocation(*it, invalidLoc);
405 }
406
407 if (!m_qmldirFiles.isEmpty())
409
410 // Pulling in the modules and neighboring qml files of the qmltypes we're trying to lint is not
411 // something we need to do.
412 if (!m_logger->fileName().endsWith(u".qmltypes"_s)) {
413 QQmlJS::ContextualTypes fromDirectory =
415 m_rootScopeImports.addTypes(std::move(fromDirectory));
416
417 // Import all possible resource directories the file may belong to.
418 // This is somewhat fuzzy, but if you're mapping the same file to multiple resource
419 // locations, you're on your own anyway.
421 const QStringList resourcePaths = mapper->resourcePaths(QQmlJSResourceFileMapper::Filter {
423 for (const QString &path : resourcePaths) {
424 const qsizetype lastSlash = path.lastIndexOf(QLatin1Char('/'));
425 if (lastSlash == -1)
426 continue;
428 }
429 }
430 }
431
432 processImportWarnings(QStringLiteral("base modules"));
433}
434
436{
437 importBaseModules();
438 return true;
439}
440
442{
443 for (const auto &scope : m_objectBindingScopes) {
445 checkDeprecation(scope);
446 }
447
448 for (const auto &scope : m_objectDefinitionScopes) {
449 if (m_pendingDefaultProperties.contains(scope))
450 continue; // We're going to check this one below.
452 checkDeprecation(scope);
453 }
454
455 for (const auto &scope : m_pendingDefaultProperties.keys()) {
457 checkDeprecation(scope);
458 }
459
460 resolveAliasesAndIds();
461
462 for (const auto &scope : m_objectDefinitionScopes)
464
472
473 auto unusedImports = m_importLocations;
474 for (const QString &type : m_usedTypes) {
475 for (const auto &importLocation : m_importTypeLocationMap.values(type))
476 unusedImports.remove(importLocation);
477
478 // If there are no more unused imports left we can abort early
479 if (unusedImports.isEmpty())
480 break;
481 }
482
484 unusedImports.remove(import);
485
486 for (const auto &import : unusedImports) {
487 m_logger->log(QString::fromLatin1("Unused import"), qmlUnusedImports, import);
488 }
489
491}
492
494{
495 ExpressionStatement *expr = cast<ExpressionStatement *>(statement);
496
497 if (!statement || !expr->expression)
498 return {};
499
500 switch (expr->expression->kind) {
501 case Node::Kind_StringLiteral:
502 return cast<StringLiteral *>(expr->expression)->value.toString();
503 case Node::Kind_NumericLiteral:
504 return cast<NumericLiteral *>(expr->expression)->value;
505 default:
506 return {};
507 }
508}
509
511{
512
513 QVector<QQmlJSAnnotation> annotationList;
514
515 for (UiAnnotationList *item = list; item != nullptr; item = item->next) {
516 UiAnnotation *annotation = item->annotation;
517
518 QQmlJSAnnotation qqmljsAnnotation;
519 qqmljsAnnotation.name = buildName(annotation->qualifiedTypeNameId);
520
521 for (UiObjectMemberList *memberItem = annotation->initializer->members; memberItem != nullptr; memberItem = memberItem->next) {
522 switch (memberItem->member->kind) {
523 case Node::Kind_UiScriptBinding: {
524 auto *scriptBinding = QQmlJS::AST::cast<UiScriptBinding*>(memberItem->member);
525 qqmljsAnnotation.bindings[buildName(scriptBinding->qualifiedId)]
526 = bindingToVariant(scriptBinding->statement);
527 break;
528 }
529 default:
530 // We ignore all the other information contained in the annotation
531 break;
532 }
533 }
534
535 annotationList.append(qqmljsAnnotation);
536 }
537
538 return annotationList;
539}
540
542{
543 for (auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) {
544 // ensure the scope is resolved, if not - it is an error
545 auto type = it->owner;
546 if (!type->isFullyResolved()) {
547 if (!type->isInCustomParserParent()) { // special otherwise
548 m_logger->log(QStringLiteral("'%1' is used but it is not resolved")
549 .arg(getScopeName(type, type->scopeType())),
550 qmlUnresolvedType, type->sourceLocation());
551 }
552 continue;
553 }
554 auto binding = it->create();
555 if (binding.isValid())
556 type->addOwnPropertyBinding(binding, it->specifier);
557 }
558}
559
561{
562 for (auto it = m_pendingDefaultProperties.constBegin();
563 it != m_pendingDefaultProperties.constEnd(); ++it) {
564 QQmlJSScope::ConstPtr parentScope = it.key();
565
566 // We can't expect custom parser default properties to be sensible, discard them for now.
567 if (parentScope->isInCustomParserParent())
568 continue;
569
570 /* consider:
571 *
572 * QtObject { // <- parentScope
573 * default property var p // (1)
574 * QtObject {} // (2)
575 * }
576 *
577 * `p` (1) is a property of a subtype of QtObject, it couldn't be used
578 * in a property binding (2)
579 */
580 // thus, use a base type of parent scope to detect a default property
581 parentScope = parentScope->baseType();
582
583 const QString defaultPropertyName =
584 parentScope ? parentScope->defaultPropertyName() : QString();
585
586 if (defaultPropertyName.isEmpty()) {
587 // If the parent scope is based on Component it can have any child element
588 // TODO: We should also store these somewhere
589 bool isComponent = false;
590 for (QQmlJSScope::ConstPtr s = parentScope; s; s = s->baseType()) {
591 if (s->internalName() == QStringLiteral("QQmlComponent")) {
592 isComponent = true;
593 break;
594 }
595 }
596
597 if (!isComponent) {
598 m_logger->log(QStringLiteral("Cannot assign to non-existent default property"),
599 qmlMissingProperty, it.value().constFirst()->sourceLocation());
600 }
601
602 continue;
603 }
604
605 const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
606 auto propType = defaultProp.type();
607 const auto handleUnresolvedDefaultProperty = [&](const QQmlJSScope::ConstPtr &) {
608 // Property type is not fully resolved we cannot tell any more than this
609 m_logger->log(QStringLiteral("Property \"%1\" has incomplete type \"%2\". You may be "
610 "missing an import.")
611 .arg(defaultPropertyName)
612 .arg(defaultProp.typeName()),
613 qmlMissingProperty, it.value().constFirst()->sourceLocation());
614 };
615
616 if (propType.isNull()) {
617 handleUnresolvedDefaultProperty(propType);
618 continue;
619 }
620
621 if (it.value().size() > 1
622 && !defaultProp.isList()
623 && !propType->isListProperty()) {
624 m_logger->log(
625 QStringLiteral("Cannot assign multiple objects to a default non-list property"),
626 qmlNonListProperty, it.value().constFirst()->sourceLocation());
627 }
628
629 if (!isTypeResolved(propType, handleUnresolvedDefaultProperty))
630 continue;
631
632 for (const QQmlJSScope::Ptr &scope : std::as_const(*it)) {
633 if (!isTypeResolved(scope))
634 continue;
635
636 // Assigning any element to a QQmlComponent property implicitly wraps it into a Component
637 // Check whether the property can be assigned the scope
638 if (propType->canAssign(scope)) {
639 scope->setIsWrappedInImplicitComponent(
640 causesImplicitComponentWrapping(defaultProp, scope));
641 continue;
642 }
643
644 m_logger->log(QStringLiteral("Cannot assign to default property of incompatible type"),
645 qmlIncompatibleType, scope->sourceLocation());
646 }
647 }
648}
649
651{
653 Q_ASSERT(type.scope->hasOwnProperty(type.name));
654
655 auto property = type.scope->ownProperty(type.name);
656
657 if (const auto propertyType =
659 property.setType(propertyType);
660 type.scope->addOwnProperty(property);
661 } else {
662 m_logger->log(property.typeName()
663 + QStringLiteral(" was not found. Did you add all import paths?"),
664 qmlImport, type.location);
665 }
666 }
667}
668
670{
671 for (const auto &type : m_pendingMethodTypes) {
672
673 for (auto [it, end] = type.scope->mutableOwnMethodsRange(type.methodName); it != end;
674 ++it) {
675 if (const auto returnType =
676 QQmlJSScope::findType(it->returnTypeName(), m_rootScopeImports).scope) {
677 it->setReturnType({ returnType });
678 } else {
679 m_logger->log(u"\"%1\" was not found for the return type of method \"%2\"."_s.arg(
680 it->returnTypeName(), it->methodName()),
681 qmlUnresolvedType, type.location);
682 }
683
684 for (auto [parameter, parameterEnd] = it->mutableParametersRange();
685 parameter != parameterEnd; ++parameter) {
686 if (const auto parameterType =
687 QQmlJSScope::findType(parameter->typeName(), m_rootScopeImports)
688 .scope) {
689 parameter->setType({ parameterType });
690 } else {
691 m_logger->log(
692 u"\"%1\" was not found for the type of parameter \"%2\" in method \"%3\"."_s
693 .arg(parameter->typeName(), parameter->name(),
694 it->methodName()),
695 qmlUnresolvedType, type.location);
696 }
697 }
698 }
699 }
700}
701
703{
704 QSet<QPair<QQmlJSScope::Ptr, QString>> foundLiterals;
705 {
706 // Note: populating literals here is special, because we do not store
707 // them in m_pendingPropertyObjectBindings, so we have to lookup all
708 // bindings on a property for each scope and see if there are any
709 // literal bindings there. this is safe to do once at the beginning
710 // because this function doesn't add new literal bindings and all
711 // literal bindings must already be added at this point.
712 QSet<QPair<QQmlJSScope::Ptr, QString>> visited;
713 for (const PendingPropertyObjectBinding &objectBinding :
714 std::as_const(m_pendingPropertyObjectBindings)) {
715 // unique because it's per-scope and per-property
716 const auto uniqueBindingId = qMakePair(objectBinding.scope, objectBinding.name);
717 if (visited.contains(uniqueBindingId))
718 continue;
719 visited.insert(uniqueBindingId);
720
721 auto [existingBindingsBegin, existingBindingsEnd] =
722 uniqueBindingId.first->ownPropertyBindings(uniqueBindingId.second);
723 const bool hasLiteralBindings =
724 std::any_of(existingBindingsBegin, existingBindingsEnd,
725 [](const QQmlJSMetaPropertyBinding &x) { return x.hasLiteral(); });
726 if (hasLiteralBindings)
727 foundLiterals.insert(uniqueBindingId);
728 }
729 }
730
731 QSet<QPair<QQmlJSScope::Ptr, QString>> foundObjects;
732 QSet<QPair<QQmlJSScope::Ptr, QString>> foundInterceptors;
733 QSet<QPair<QQmlJSScope::Ptr, QString>> foundValueSources;
734
735 for (const PendingPropertyObjectBinding &objectBinding :
736 std::as_const(m_pendingPropertyObjectBindings)) {
737 const QString propertyName = objectBinding.name;
738 QQmlJSScope::ConstPtr childScope = objectBinding.childScope;
739
740 if (!isTypeResolved(objectBinding.scope)) // guarantees property lookup
741 continue;
742
743 QQmlJSMetaProperty property = objectBinding.scope->property(propertyName);
744
745 if (!property.isValid()) {
746 m_logger->log(QStringLiteral("Property \"%1\" does not exist").arg(propertyName),
747 qmlMissingProperty, objectBinding.location);
748 continue;
749 }
750 const auto handleUnresolvedProperty = [&](const QQmlJSScope::ConstPtr &) {
751 // Property type is not fully resolved we cannot tell any more than this
752 m_logger->log(QStringLiteral("Property \"%1\" has incomplete type \"%2\". You may be "
753 "missing an import.")
754 .arg(propertyName)
755 .arg(property.typeName()),
756 qmlUnresolvedType, objectBinding.location);
757 };
758 if (property.type().isNull()) {
759 handleUnresolvedProperty(property.type());
760 continue;
761 }
762
763 // guarantee that canAssign() can be called
764 if (!isTypeResolved(property.type(), handleUnresolvedProperty)
765 || !isTypeResolved(childScope)) {
766 continue;
767 }
768
769 if (!objectBinding.onToken && !property.type()->canAssign(childScope)) {
770 // the type is incompatible
771 m_logger->log(QStringLiteral("Property \"%1\" of type \"%2\" is assigned an "
772 "incompatible type \"%3\"")
773 .arg(propertyName)
774 .arg(property.typeName())
775 .arg(getScopeName(childScope, QQmlSA::ScopeType::QMLScope)),
776 qmlIncompatibleType, objectBinding.location);
777 continue;
778 }
779
780 objectBinding.childScope->setIsWrappedInImplicitComponent(
782
783 // unique because it's per-scope and per-property
784 const auto uniqueBindingId = qMakePair(objectBinding.scope, objectBinding.name);
786
787 if (objectBinding.onToken) {
788 if (childScope->hasInterface(QStringLiteral("QQmlPropertyValueInterceptor"))) {
789 if (foundInterceptors.contains(uniqueBindingId)) {
790 m_logger->log(QStringLiteral("Duplicate interceptor on property \"%1\"")
791 .arg(propertyName),
792 qmlDuplicatePropertyBinding, objectBinding.location);
793 } else {
794 foundInterceptors.insert(uniqueBindingId);
795 }
796 } else if (childScope->hasInterface(QStringLiteral("QQmlPropertyValueSource"))) {
797 if (foundValueSources.contains(uniqueBindingId)) {
798 m_logger->log(QStringLiteral("Duplicate value source on property \"%1\"")
799 .arg(propertyName),
800 qmlDuplicatePropertyBinding, objectBinding.location);
801 } else if (foundObjects.contains(uniqueBindingId)
802 || foundLiterals.contains(uniqueBindingId)) {
803 m_logger->log(QStringLiteral("Cannot combine value source and binding on "
804 "property \"%1\"")
805 .arg(propertyName),
806 qmlDuplicatePropertyBinding, objectBinding.location);
807 } else {
808 foundValueSources.insert(uniqueBindingId);
809 }
810 } else {
811 m_logger->log(QStringLiteral("On-binding for property \"%1\" has wrong type \"%2\"")
812 .arg(propertyName)
813 .arg(typeName),
814 qmlIncompatibleType, objectBinding.location);
815 }
816 } else {
817 // TODO: Warn here if binding.hasValue() is true
818 if (foundValueSources.contains(uniqueBindingId)) {
819 m_logger->log(
820 QStringLiteral("Cannot combine value source and binding on property \"%1\"")
821 .arg(propertyName),
822 qmlDuplicatePropertyBinding, objectBinding.location);
823 } else {
824 foundObjects.insert(uniqueBindingId);
825 }
826 }
827 }
828}
829
831{
832 for (const auto &required : m_requiredProperties) {
833 if (!required.scope->hasProperty(required.name)) {
834 m_logger->log(
835 QStringLiteral("Property \"%1\" was marked as required but does not exist.")
836 .arg(required.name),
837 qmlRequired, required.location);
838 }
839 }
840
841 for (const auto &defScope : m_objectDefinitionScopes) {
842 if (defScope->parentScope() == m_globalScope || defScope->isInlineComponent() || defScope->isComponentRootElement())
843 continue;
844
845 QVector<QQmlJSScope::ConstPtr> scopesToSearch;
846 for (QQmlJSScope::ConstPtr scope = defScope; scope; scope = scope->baseType()) {
847 scopesToSearch << scope;
848 const auto ownProperties = scope->ownProperties();
849 for (auto propertyIt = ownProperties.constBegin();
850 propertyIt != ownProperties.constEnd(); ++propertyIt) {
851 const QString propName = propertyIt.key();
852
853 QQmlJSScope::ConstPtr prevRequiredScope;
854 for (QQmlJSScope::ConstPtr requiredScope : scopesToSearch) {
855 if (requiredScope->isPropertyLocallyRequired(propName)) {
856 bool found =
857 std::find_if(scopesToSearch.constBegin(), scopesToSearch.constEnd(),
858 [&](QQmlJSScope::ConstPtr scope) {
859 return scope->hasPropertyBindings(propName);
860 })
861 != scopesToSearch.constEnd();
862
863 if (!found) {
864 const QString scopeId = m_scopesById.id(defScope, scope);
865 bool propertyUsedInRootAlias = false;
866 if (!scopeId.isEmpty()) {
867 for (const QQmlJSMetaProperty &property :
869 if (!property.isAlias())
870 continue;
871
872 QStringList aliasExpression =
873 property.aliasExpression().split(u'.');
874
875 if (aliasExpression.size() != 2)
876 continue;
877 if (aliasExpression[0] == scopeId
878 && aliasExpression[1] == propName) {
879 propertyUsedInRootAlias = true;
880 break;
881 }
882 }
883 }
884
885 if (propertyUsedInRootAlias)
886 continue;
887
888 const QQmlJSScope::ConstPtr propertyScope = scopesToSearch.size() > 1
889 ? scopesToSearch.at(scopesToSearch.size() - 2)
891
892 const QString propertyScopeName = !propertyScope.isNull()
894 : u"here"_s;
895
896 const QString requiredScopeName = prevRequiredScope
897 ? getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope)
898 : u"here"_s;
899
900 std::optional<QQmlJSFixSuggestion> suggestion;
901
904 "Component is missing required property %1 from %2")
905 .arg(propName)
906 .arg(propertyScopeName);
907 if (requiredScope != scope) {
908 if (!prevRequiredScope.isNull()) {
909 auto sourceScope = prevRequiredScope->baseType();
910 suggestion = QQmlJSFixSuggestion{
911 "%1:%2:%3: Property marked as required in %4."_L1
912 .arg(sourceScope->filePath())
913 .arg(sourceScope->sourceLocation().startLine)
914 .arg(sourceScope->sourceLocation().startColumn)
915 .arg(requiredScopeName),
916 sourceScope->sourceLocation()
917 };
918 suggestion->setFilename(sourceScope->filePath());
919 } else {
920 message += QStringLiteral(" (marked as required by %1)")
921 .arg(requiredScopeName);
922 }
923 }
924
925 m_logger->log(message, qmlRequired, defScope->sourceLocation(), true,
926 true, suggestion);
927 }
928 }
929 prevRequiredScope = requiredScope;
930 }
931 }
932 }
933 }
934}
935
937{
938 for (auto it = m_propertyBindings.constBegin(); it != m_propertyBindings.constEnd(); ++it) {
939 QQmlJSScope::Ptr scope = it.key();
940 for (auto &[visibilityScope, location, name] : it.value()) {
941 if (!scope->hasProperty(name)) {
942 // These warnings do not apply for custom parsers and their children and need to be
943 // handled on a case by case basis
944
945 if (scope->isInCustomParserParent())
946 continue;
947
948 // TODO: Can this be in a better suited category?
949 std::optional<QQmlJSFixSuggestion> fixSuggestion;
950
951 for (QQmlJSScope::ConstPtr baseScope = scope; !baseScope.isNull();
952 baseScope = baseScope->baseType()) {
953 if (auto suggestion = QQmlJSUtils::didYouMean(
954 name, baseScope->ownProperties().keys(), location);
955 suggestion.has_value()) {
956 fixSuggestion = suggestion;
957 break;
958 }
959 }
960
961 m_logger->log(QStringLiteral("Binding assigned to \"%1\", but no property \"%1\" "
962 "exists in the current element.")
963 .arg(name),
964 qmlMissingProperty, location, true, true, fixSuggestion);
965 continue;
966 }
967
968 const auto property = scope->property(name);
969 if (!property.type()) {
970 m_logger->log(QStringLiteral("No type found for property \"%1\". This may be due "
971 "to a missing import statement or incomplete "
972 "qmltypes files.")
973 .arg(name),
975 }
976
977 const auto &annotations = property.annotations();
978
979 const auto deprecationAnn =
980 std::find_if(annotations.cbegin(), annotations.cend(),
981 [](const QQmlJSAnnotation &ann) { return ann.isDeprecation(); });
982
983 if (deprecationAnn != annotations.cend()) {
984 const auto deprecation = deprecationAnn->deprecation();
985
986 QString message = QStringLiteral("Binding on deprecated property \"%1\"")
987 .arg(property.propertyName());
988
989 if (!deprecation.reason.isEmpty())
990 message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
991
993 }
994 }
995 }
996}
997
998void QQmlJSImportVisitor::checkSignal(
999 const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location,
1000 const QString &handlerName, const QStringList &handlerParameters)
1001{
1002 const auto signal = QQmlSignalNames::handlerNameToSignalName(handlerName);
1003
1004 std::optional<QQmlJSMetaMethod> signalMethod;
1005 const auto setSignalMethod = [&](const QQmlJSScope::ConstPtr &scope, const QString &name) {
1006 const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal);
1007 if (!methods.isEmpty())
1008 signalMethod = methods[0];
1009 };
1010
1011 if (signal.has_value()) {
1012 if (signalScope->hasMethod(*signal)) {
1013 setSignalMethod(signalScope, *signal);
1014 } else if (auto p = QQmlJSUtils::propertyFromChangedHandler(signalScope, handlerName)) {
1015 // we have a change handler of the form "onXChanged" where 'X'
1016 // is a property name
1017
1018 // NB: qqmltypecompiler prefers signal to bindable
1019 if (auto notify = p->notify(); !notify.isEmpty()) {
1020 setSignalMethod(signalScope, notify);
1021 } else {
1022 Q_ASSERT(!p->bindable().isEmpty());
1023 signalMethod = QQmlJSMetaMethod {}; // use dummy in this case
1024 }
1025 }
1026 }
1027
1028 if (!signalMethod.has_value()) { // haven't found anything
1029 std::optional<QQmlJSFixSuggestion> fix;
1030
1031 // There is a small chance of suggesting this fix for things that are not actually
1032 // QtQml/Connections elements, but rather some other thing that is also called
1033 // "Connections". However, I guess we can live with this.
1034 if (signalScope->baseTypeName() == QStringLiteral("Connections")) {
1035
1036 // Cut to the end of the line to avoid hairy issues with pre-existing function()
1037 // and the colon.
1038 const qsizetype newLength = m_logger->code().indexOf(u'\n', location.end())
1039 - location.offset;
1040
1041 fix = QQmlJSFixSuggestion{
1042 "Implicitly defining %1 as signal handler in Connections is deprecated. "
1043 "Create a function instead."_L1.arg(handlerName),
1044 QQmlJS::SourceLocation(location.offset, newLength, location.startLine,
1045 location.startColumn),
1046 "function %1(%2) { ... }"_L1.arg(handlerName, handlerParameters.join(u", "))
1047 };
1048 }
1049
1050 m_logger->log(QStringLiteral("no matching signal found for handler \"%1\"")
1051 .arg(handlerName),
1052 qmlUnqualified, location, true, true, fix);
1053 return;
1054 }
1055
1056 const auto signalParameters = signalMethod->parameters();
1057 QHash<QString, qsizetype> parameterNameIndexes;
1058 // check parameter positions and also if signal is suitable for onSignal handler
1059 for (int i = 0, end = signalParameters.size(); i < end; i++) {
1060 auto &p = signalParameters[i];
1061 parameterNameIndexes[p.name()] = i;
1062
1063 auto signalName = [&]() {
1064 if (signal)
1065 return u" called %1"_s.arg(*signal);
1066 return QString();
1067 };
1068 auto type = p.type();
1069 if (!type) {
1070 m_logger->log(
1072 "Type %1 of parameter %2 in signal%3 was not found, but is "
1073 "required to compile %4. Did you add all import paths?")
1074 .arg(p.typeName(), p.name(), signalName(), handlerName),
1076 continue;
1077 }
1078
1079 if (type->isComposite())
1080 continue;
1081
1082 // only accept following parameters for non-composite types:
1083 // * QObjects by pointer (nonconst*, const*, const*const,*const)
1084 // * Value types by value (QFont, int)
1085 // * Value types by const ref (const QFont&, const int&)
1086
1087 auto parameterName = [&]() {
1088 if (p.name().isEmpty())
1089 return QString();
1090 return u" called %1"_s.arg(p.name());
1091 };
1092 switch (type->accessSemantics()) {
1093 case QQmlJSScope::AccessSemantics::Reference:
1094 if (!p.isPointer())
1095 m_logger->log(QStringLiteral("Type %1 of parameter%2 in signal%3 should be "
1096 "passed by pointer to be able to compile %4. ")
1097 .arg(p.typeName(), parameterName(), signalName(),
1098 handlerName),
1100 break;
1101 case QQmlJSScope::AccessSemantics::Value:
1102 case QQmlJSScope::AccessSemantics::Sequence:
1103 if (p.isPointer())
1104 m_logger->log(
1106 "Type %1 of parameter%2 in signal%3 should be passed by "
1107 "value or const reference to be able to compile %4. ")
1108 .arg(p.typeName(), parameterName(), signalName(),
1109 handlerName),
1111 break;
1112 case QQmlJSScope::AccessSemantics::None:
1113 m_logger->log(
1114 QStringLiteral("Type %1 of parameter%2 in signal%3 required by the "
1115 "compilation of %4 cannot be used. ")
1116 .arg(p.typeName(), parameterName(), signalName(), handlerName),
1118 break;
1119 }
1120 }
1121
1122 if (handlerParameters.size() > signalParameters.size()) {
1123 m_logger->log(QStringLiteral("Signal handler for \"%2\" has more formal"
1124 " parameters than the signal it handles.")
1125 .arg(handlerName),
1127 return;
1128 }
1129
1130 for (qsizetype i = 0, end = handlerParameters.size(); i < end; i++) {
1131 const QStringView handlerParameter = handlerParameters.at(i);
1132 auto it = parameterNameIndexes.constFind(handlerParameter.toString());
1133 if (it == parameterNameIndexes.constEnd())
1134 continue;
1135 const qsizetype j = *it;
1136
1137 if (j == i)
1138 continue;
1139
1140 m_logger->log(QStringLiteral("Parameter %1 to signal handler for \"%2\""
1141 " is called \"%3\". The signal has a parameter"
1142 " of the same name in position %4.")
1143 .arg(i + 1)
1144 .arg(handlerName, handlerParameter)
1145 .arg(j + 1),
1147 }
1148}
1149
1151{
1153 if (m_currentScope == m_exportedRootScope || parentScope->isArrayScope()
1154 || m_currentScope->isInlineComponent()) // inapplicable
1155 return;
1156
1158
1159 if (parentScope->isInCustomParserParent())
1160 return;
1161
1162 /* consider:
1163 *
1164 * QtObject { // <- parentScope
1165 * default property var p // (1)
1166 * QtObject {} // (2)
1167 * }
1168 *
1169 * `p` (1) is a property of a subtype of QtObject, it couldn't be used
1170 * in a property binding (2)
1171 */
1172 // thus, use a base type of parent scope to detect a default property
1173 parentScope = parentScope->baseType();
1174
1175 const QString defaultPropertyName =
1176 parentScope ? parentScope->defaultPropertyName() : QString();
1177
1178 if (defaultPropertyName.isEmpty()) // an error somewhere else
1179 return;
1180
1181 // Note: in this specific code path, binding on default property
1182 // means an object binding (we work with pending objects here)
1183 QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName);
1186 m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() { return binding; },
1188}
1189
1191{
1192 QList<QQmlJSScope::ConstPtr> scopes;
1193 for (QQmlJSScope::ConstPtr scope = originalScope; scope;) {
1194 if (scopes.contains(scope)) {
1195 QString inheritenceCycle;
1196 for (const auto &seen : std::as_const(scopes)) {
1197 inheritenceCycle.append(seen->baseTypeName());
1198 inheritenceCycle.append(QLatin1String(" -> "));
1199 }
1200 inheritenceCycle.append(scopes.first()->baseTypeName());
1201
1202 const QString message = QStringLiteral("%1 is part of an inheritance cycle: %2")
1203 .arg(scope->internalName(), inheritenceCycle);
1205 originalScope->clearBaseType();
1206 originalScope->setBaseTypeError(message);
1207 break;
1208 }
1209
1210 scopes.append(scope);
1211
1212 const auto newScope = scope->baseType();
1213 if (newScope.isNull()) {
1214 const QString error = scope->baseTypeError();
1215 const QString name = scope->baseTypeName();
1216 if (!error.isEmpty()) {
1217 m_logger->log(error, qmlImport, scope->sourceLocation(), true, true);
1218 } else if (!name.isEmpty()) {
1219 m_logger->log(
1220 name + QStringLiteral(" was not found. Did you add all import paths?"),
1221 qmlImport, scope->sourceLocation(), true, true,
1223 m_rootScopeImports.types().keys(),
1224 scope->sourceLocation()));
1225 }
1226 }
1227
1228 scope = newScope;
1229 }
1230}
1231
1233{
1234 for (QQmlJSScope::ConstPtr scope = originalScope; scope; scope = scope->baseType()) {
1235 for (const QQmlJSAnnotation &annotation : scope->annotations()) {
1236 if (annotation.isDeprecation()) {
1237 QQQmlJSDeprecation deprecation = annotation.deprecation();
1238
1240 QStringLiteral("Type \"%1\" is deprecated").arg(scope->internalName());
1241
1242 if (!deprecation.reason.isEmpty())
1243 message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
1244
1245 m_logger->log(message, qmlDeprecated, originalScope->sourceLocation());
1246 }
1247 }
1248 }
1249}
1250
1252{
1253 // These warnings do not apply for custom parsers and their children and need to be handled on a
1254 // case by case basis
1255 if (scope->isInCustomParserParent())
1256 return;
1257
1258 auto children = scope->childScopes();
1259 while (!children.isEmpty()) {
1260 auto childScope = children.takeFirst();
1261 const auto type = childScope->scopeType();
1262 switch (type) {
1265 if (!childScope->baseType()) {
1266 m_logger->log(QStringLiteral("unknown %1 property scope %2.")
1268 ? QStringLiteral("grouped")
1269 : QStringLiteral("attached"),
1270 childScope->internalName()),
1271 qmlUnqualified, childScope->sourceLocation());
1272 }
1273 children.append(childScope->childScopes());
1274 break;
1275 default:
1276 break;
1277 }
1278 }
1279}
1280
1291
1301 const QString &name)
1302{
1303 auto &array = m_functionsAndExpressions[scope];
1304 array.emplaceBack(name);
1305
1306 // add current function to all preceding functions in the stack. we don't
1307 // know which one is going to be the "publicly visible" one, so just blindly
1308 // add it to every level and let further logic take care of that. this
1309 // matches what m_innerFunctions represents as function at each level just
1310 // got a new inner function
1311 for (const auto &function : m_functionStack)
1312 m_innerFunctions[function]++;
1313 m_functionStack.push({ scope, name }); // create new function
1314
1315 return QQmlJSMetaMethod::RelativeFunctionIndex { int(array.size() - 1) };
1316}
1317
1328{
1329 auto nameToVerify = name.isEmpty() ? u"<anon>"_s : name;
1330 Q_UNUSED(nameToVerify);
1331 Q_ASSERT(!m_functionStack.isEmpty());
1332 Q_ASSERT(m_functionStack.top().name == nameToVerify);
1333 m_functionStack.pop();
1334}
1335
1349 const QQmlJSScope::Ptr &scope, int count) const
1350{
1351 const auto suitableScope = [](const QQmlJSScope::Ptr &scope) {
1352 const auto type = scope->scopeType();
1356 };
1357
1358 if (!suitableScope(scope))
1359 return count;
1360
1361 QList<QQmlJSMetaMethod::AbsoluteFunctionIndex> indices;
1362 auto it = m_functionsAndExpressions.constFind(scope);
1363 if (it == m_functionsAndExpressions.cend()) // scope has no runtime functions
1364 return count;
1365
1366 const auto &functionsAndExpressions = *it;
1367 for (const QString &functionOrExpression : functionsAndExpressions) {
1370 ++count;
1371
1372 // there are special cases: onSignal: function() { doSomethingUsefull }
1373 // in which we would register 2 functions in the runtime functions table
1374 // for the same expression. even more, we can have named and unnamed
1375 // closures inside a function or a script binding e.g.:
1376 // ```
1377 // function foo() {
1378 // var closure = () => { return 42; }; // this is an inner function
1379 // /* or:
1380 // property = Qt.binding(function() { return anotherProperty; });
1381 // */
1382 // return closure();
1383 // }
1384 // ```
1385 // see Codegen::defineFunction() in qv4codegen.cpp for more details
1386 count += m_innerFunctions.value({ scope, functionOrExpression }, 0);
1387 }
1388
1389 return count;
1390}
1391
1393{
1394 int count = 0;
1395 const auto synthesize = [&](const QQmlJSScope::Ptr &current) {
1397 };
1399}
1400
1410
1418
1420{
1421 const QString s = m_logger->code().mid(sl->literalToken.begin(), sl->literalToken.length);
1422
1423 if (s.contains(QLatin1Char('\r')) || s.contains(QLatin1Char('\n')) || s.contains(QChar(0x2028u))
1424 || s.contains(QChar(0x2029u))) {
1425 QString templateString;
1426
1427 bool escaped = false;
1428 const QChar stringQuote = s[0];
1429 for (qsizetype i = 1; i < s.size() - 1; i++) {
1430 const QChar c = s[i];
1431
1432 if (c == u'\\') {
1433 escaped = !escaped;
1434 } else if (escaped) {
1435 // If we encounter an escaped quote, unescape it since we use backticks here
1436 if (c == stringQuote)
1437 templateString.chop(1);
1438
1439 escaped = false;
1440 } else {
1441 if (c == u'`')
1442 templateString += u'\\';
1443 if (c == u'$' && i + 1 < s.size() - 1 && s[i + 1] == u'{')
1444 templateString += u'\\';
1445 }
1446
1447 templateString += c;
1448 }
1449
1450 QQmlJSFixSuggestion suggestion = { "Use a template literal instead."_L1, sl->literalToken,
1451 u"`" % templateString % u"`" };
1452 suggestion.setAutoApplicable();
1453 m_logger->log(QStringLiteral("String contains unescaped line terminator which is "
1454 "deprecated."),
1455 qmlMultilineStrings, sl->literalToken, true, true, suggestion);
1456 }
1457
1458 return true;
1459}
1460
1463 const QQmlJS::SourceLocation &srcLocation);
1464
1466 QQmlJSLogger *logger)
1467{
1468 QStringView namespaceName{ superType };
1469 namespaceName = namespaceName.first(namespaceName.indexOf(u'.'));
1470 logger->log(u"Namespace '%1' of '%2' must start with an upper case letter."_s.arg(namespaceName)
1471 .arg(superType),
1472 qmlUncreatableType, location, true, true);
1473}
1474
1476{
1477 const QString superType = buildName(definition->qualifiedTypeNameId);
1478
1479 const bool isRoot = !rootScopeIsValid();
1480 Q_ASSERT(!superType.isEmpty());
1481
1482 // we need to assume that it is a type based on its capitalization. Types defined in inline
1483 // components, for example, can have their type definition after their type usages:
1484 // Item { property IC myIC; component IC: Item{}; }
1485 const qsizetype indexOfTypeName = superType.lastIndexOf(u'.');
1486 const bool looksLikeGroupedProperty = superType.front().isLower();
1487
1488 if (indexOfTypeName != -1 && looksLikeGroupedProperty) {
1489 logLowerCaseImport(superType, definition->qualifiedTypeNameId->identifierToken,
1490 m_logger);
1491 }
1492
1493 if (!looksLikeGroupedProperty) {
1494 if (!isRoot) {
1496 definition->firstSourceLocation());
1497 } else {
1498 enterRootScope(QQmlSA::ScopeType::QMLScope, superType,
1499 definition->firstSourceLocation());
1501 }
1502
1505 if (auto base = m_currentScope->baseType(); base) {
1506 if (isRoot && base->internalName() == u"QQmlComponent") {
1507 m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
1508 definition->qualifiedTypeNameId->identifierToken, true, true);
1509 }
1510 if (base->isSingleton() && m_currentScope->isComposite()) {
1511 m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
1513 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1514 true, true);
1515
1516 } else if (!base->isCreatable()) {
1517 // composite type m_currentScope is allowed to be uncreatable, but it cannot be the base of anything else
1518 m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
1519 qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
1520 true, true);
1521 }
1522 }
1524 Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
1525 const QString &name = std::get<InlineComponentNameType>(m_currentRootName);
1531 }
1532
1535 m_qmlTypes.append(m_currentScope);
1536
1538 } else {
1540 definition->firstSourceLocation());
1542 definition->firstSourceLocation()));
1544 }
1545
1546 m_currentScope->setAnnotations(parseAnnotations(definition->annotations));
1547
1548 return true;
1549}
1550
1556
1558{
1559 if (!std::holds_alternative<RootDocumentNameType>(m_currentRootName)) {
1560 m_logger->log(u"Nested inline components are not supported"_s, qmlSyntax,
1561 component->firstSourceLocation());
1562 return true;
1563 }
1564
1566 m_currentRootName = component->name.toString();
1567 return true;
1568}
1569
1571{
1574 m_logger->log(u"Inline component declaration must be followed by a typename"_s,
1575 qmlSyntax, component->firstSourceLocation());
1576 }
1577 m_nextIsInlineComponent = false; // might have missed an inline component if file contains invalid QML
1578}
1579
1581{
1582 switch (publicMember->type) {
1584 if (m_currentScope->ownMethods().contains(publicMember->name.toString())) {
1585 m_logger->log(QStringLiteral("Duplicated signal name \"%1\".").arg(
1586 publicMember->name.toString()), qmlDuplicatedName,
1587 publicMember->firstSourceLocation());
1588 }
1589 UiParameterList *param = publicMember->parameters;
1591 method.setMethodType(QQmlJSMetaMethodType::Signal);
1592 method.setMethodName(publicMember->name.toString());
1593 method.setSourceLocation(combine(publicMember->firstSourceLocation(),
1594 publicMember->lastSourceLocation()));
1595 while (param) {
1596 method.addParameter(
1598 param->name.toString(),
1599 param->type ? param->type->toString() : QString()
1600 ));
1601 param = param->next;
1602 }
1604 break;
1605 }
1607 if (m_currentScope->ownProperties().contains(publicMember->name.toString())) {
1608 m_logger->log(QStringLiteral("Duplicated property name \"%1\".").arg(
1609 publicMember->name.toString()), qmlDuplicatedName,
1610 publicMember->firstSourceLocation());
1611 }
1612 QString typeName = buildName(publicMember->memberType);
1613 if (typeName.contains(u'.') && typeName.front().isLower()) {
1614 logLowerCaseImport(typeName, publicMember->typeToken, m_logger);
1615 }
1616
1617 QString aliasExpr;
1618 const bool isAlias = (typeName == u"alias"_s);
1619 if (isAlias) {
1620 auto tryParseAlias = [&]() {
1621 typeName.clear(); // type name is useless for alias here, so keep it empty
1622 if (!publicMember->statement) {
1623 m_logger->log(QStringLiteral("Invalid alias expression – an initalizer is needed."),
1624 qmlSyntax, publicMember->memberType->firstSourceLocation()); // TODO: extend warning to cover until endSourceLocation
1625 return;
1626 }
1627 const auto expression = cast<ExpressionStatement *>(publicMember->statement);
1628 auto node = expression ? expression->expression : nullptr;
1629 auto fex = cast<FieldMemberExpression *>(node);
1630 while (fex) {
1631 node = fex->base;
1632 aliasExpr.prepend(u'.' + fex->name.toString());
1633 fex = cast<FieldMemberExpression *>(node);
1634 }
1635
1636 if (const auto idExpression = cast<IdentifierExpression *>(node)) {
1637 aliasExpr.prepend(idExpression->name.toString());
1638 } else {
1639 // cast to expression might have failed above, so use publicMember->statement
1640 // to obtain the source location
1641 m_logger->log(QStringLiteral("Invalid alias expression. Only IDs and field "
1642 "member expressions can be aliased."),
1643 qmlSyntax, publicMember->statement->firstSourceLocation());
1644 }
1645 };
1646 tryParseAlias();
1647 } else {
1649 && !m_rootScopeImports.type(typeName).scope.isNull()) {
1652 }
1653 }
1654 QQmlJSMetaProperty prop;
1655 prop.setPropertyName(publicMember->name.toString());
1656 prop.setIsList(publicMember->typeModifier == QLatin1String("list"));
1657 prop.setIsWritable(!publicMember->isReadonly());
1658 prop.setAliasExpression(aliasExpr);
1659 const auto type =
1661 if (type) {
1662 prop.setType(prop.isList() ? type->listType() : type);
1663 const QString internalName = type->internalName();
1664 prop.setTypeName(internalName.isEmpty() ? typeName : internalName);
1665 } else if (!isAlias) {
1667 publicMember->firstSourceLocation() };
1668 prop.setTypeName(typeName);
1669 }
1670 prop.setAnnotations(parseAnnotations(publicMember->annotations));
1671 if (publicMember->isDefaultMember())
1675 if (publicMember->isRequired())
1677
1679 // if property is an alias, initialization expression is not a binding
1680 if (!isAlias) {
1681 parseResult =
1682 parseBindingExpression(publicMember->name.toString(), publicMember->statement);
1683 }
1684
1685 // however, if we have a property with a script binding assigned to it,
1686 // we have to create a new scope
1687 if (parseResult == BindingExpressionParseResult::Script) {
1688 Q_ASSERT(!m_savedBindingOuterScope); // automatically true due to grammar
1691 publicMember->statement->firstSourceLocation());
1692 }
1693
1694 break;
1695 }
1696 }
1697
1698 return true;
1699}
1700
1702{
1706 // m_savedBindingOuterScope is only set if we encounter a script binding
1707 forgetFunctionExpression(publicMember->name.toString());
1708 }
1709}
1710
1712{
1713 const QString name = required->name.toString();
1714
1716 required->firstSourceLocation() };
1717
1719 return true;
1720}
1721
1722void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
1723{
1724 using namespace QQmlJS::AST;
1725 auto name = fexpr->name.toString();
1726 bool pending = false;
1727 if (!name.isEmpty()) {
1729 method.setMethodType(QQmlJSMetaMethodType::Method);
1730 method.setSourceLocation(combine(fexpr->firstSourceLocation(), fexpr->lastSourceLocation()));
1731
1732 if (!m_pendingMethodAnnotations.isEmpty()) {
1733 method.setAnnotations(m_pendingMethodAnnotations);
1735 }
1736
1737 // If signatures are explicitly ignored, we don't parse the types
1738 const bool parseTypes = m_scopesById.signaturesAreEnforced();
1739
1740 bool formalsFullyTyped = parseTypes;
1741 bool anyFormalTyped = false;
1742 if (const auto *formals = parseTypes ? fexpr->formals : nullptr) {
1743 const auto parameters = formals->formals();
1744 for (const auto &parameter : parameters) {
1745 const QString type = parameter.typeAnnotation
1746 ? parameter.typeAnnotation->type->toString()
1747 : QString();
1748 if (type.isEmpty()) {
1749 formalsFullyTyped = false;
1750 method.addParameter(QQmlJSMetaParameter(parameter.id, QStringLiteral("var")));
1751 } else {
1752 anyFormalTyped = true;
1753 method.addParameter(QQmlJSMetaParameter(parameter.id, type));
1754 if (!pending) {
1755 m_pendingMethodTypes << PendingMethodType{
1757 name,
1758 combine(parameter.typeAnnotation->firstSourceLocation(),
1759 parameter.typeAnnotation->lastSourceLocation())
1760 };
1761 pending = true;
1762 }
1763 }
1764 }
1765 }
1766
1767 // If a function is fully typed, we can call it like a C++ function.
1768 method.setIsJavaScriptFunction(!formalsFullyTyped);
1769
1770 // Methods with explicit return type return that.
1771 // Methods with only untyped arguments return an untyped value.
1772 // Methods with at least one typed argument but no explicit return type return void.
1773 // In order to make a function without arguments return void, you have to specify that.
1774 if (parseTypes && fexpr->typeAnnotation) {
1775 method.setReturnTypeName(fexpr->typeAnnotation->type->toString());
1776 if (!pending) {
1777 m_pendingMethodTypes << PendingMethodType{
1779 combine(fexpr->typeAnnotation->firstSourceLocation(),
1780 fexpr->typeAnnotation->lastSourceLocation())
1781 };
1782 pending = true;
1783 }
1784 } else if (anyFormalTyped)
1785 method.setReturnTypeName(QStringLiteral("void"));
1786 else
1787 method.setReturnTypeName(QStringLiteral("var"));
1788
1789 method.setJsFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName()));
1791
1795 fexpr->firstSourceLocation(),
1796 method.returnTypeName(), false });
1797 }
1798 enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
1799 } else {
1802 fexpr->firstSourceLocation());
1803 }
1804}
1805
1807{
1808 visitFunctionExpressionHelper(fexpr);
1809 return true;
1810}
1811
1817
1819{
1820 m_pendingMethodAnnotations = parseAnnotations(srcElement->annotations);
1821 return true;
1822}
1823
1825{
1826 visitFunctionExpressionHelper(fdecl);
1827 return true;
1828}
1829
1835
1845
1850
1853{
1854 QStringView contextString;
1855 QStringView mainString;
1856 QStringView commentString;
1857 auto registerContextString = [&](QStringView string) {
1858 contextString = string;
1859 return 0;
1860 };
1861 auto registerMainString = [&](QStringView string) {
1862 mainString = string;
1863 return 0;
1864 };
1865 auto registerCommentString = [&](QStringView string) {
1866 commentString = string;
1867 return 0;
1868 };
1869 auto finalizeBinding = [&](QV4::CompiledData::Binding::Type type,
1872 binding.setTranslation(mainString, commentString, contextString, data.number);
1874 binding.setTranslationId(mainString, data.number);
1875 } else {
1876 binding.setStringLiteral(mainString);
1877 }
1878 };
1880 base, args,
1881 registerMainString, registerCommentString, registerContextString, finalizeBinding);
1882}
1883
1886 const QQmlJS::AST::Statement *statement)
1887{
1888 if (statement == nullptr)
1890
1891 const auto *exprStatement = cast<const ExpressionStatement *>(statement);
1892
1893 if (exprStatement == nullptr) {
1895
1896 if (const auto *block = cast<const Block *>(statement); block && block->statements) {
1897 location = block->statements->firstSourceLocation();
1898 }
1899
1905 [binding = std::move(binding)]() { return binding; }
1906 });
1908 }
1909
1910 auto expr = exprStatement->expression;
1912 combine(expr->firstSourceLocation(), expr->lastSourceLocation()),
1913 name);
1914
1915 bool isUndefinedBinding = false;
1916
1917 switch (expr->kind) {
1918 case Node::Kind_TrueLiteral:
1919 binding.setBoolLiteral(true);
1920 break;
1921 case Node::Kind_FalseLiteral:
1922 binding.setBoolLiteral(false);
1923 break;
1924 case Node::Kind_NullExpression:
1925 binding.setNullLiteral();
1926 break;
1927 case Node::Kind_IdentifierExpression: {
1928 auto idExpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr);
1929 Q_ASSERT(idExpr);
1930 isUndefinedBinding = (idExpr->name == u"undefined");
1931 break;
1932 }
1933 case Node::Kind_NumericLiteral:
1934 binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
1935 break;
1936 case Node::Kind_StringLiteral:
1937 binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
1938 break;
1939 case Node::Kind_RegExpLiteral:
1940 binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
1941 break;
1942 case Node::Kind_TemplateLiteral: {
1943 auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
1944 Q_ASSERT(templateLit);
1945 if (templateLit->hasNoSubstitution) {
1946 binding.setStringLiteral(templateLit->value);
1947 } else {
1950 for (QQmlJS::AST::TemplateLiteral *l = templateLit; l; l = l->next) {
1951 if (QQmlJS::AST::ExpressionNode *expression = l->expression)
1952 expression->accept(this);
1953 }
1954 }
1955 break;
1956 }
1957 default:
1958 if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
1959 if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
1960 binding.setNumberLiteral(-lit->value);
1961 } else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
1962 if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
1963 handleTranslationBinding(binding, base->name, call->arguments);
1964 }
1965 break;
1966 }
1967
1968 if (!binding.isValid()) {
1969 // consider this to be a script binding (see IRBuilder::setBindingValue)
1974 }
1975 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; } });
1976
1977 // translations are neither literal bindings nor script bindings
1981 }
1982 if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
1986}
1987
1989{
1990 if (prefix.isEmpty() || !prefix.front().isUpper())
1991 return false;
1992
1993 return m_rootScopeImports.isNullType(prefix);
1994}
1995
1996void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
1997{
1998 const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
1999 if (!statement) {
2000 m_logger->log(u"id must be followed by an identifier"_s, qmlSyntax,
2001 scriptBinding->statement->firstSourceLocation());
2002 return;
2003 }
2004 const QString name = [&]() {
2005 if (const auto *idExpression = cast<IdentifierExpression *>(statement->expression))
2006 return idExpression->name.toString();
2007 else if (const auto *idString = cast<StringLiteral *>(statement->expression)) {
2008 m_logger->log(u"ids do not need quotation marks"_s, qmlSyntaxIdQuotation,
2009 idString->firstSourceLocation());
2010 return idString->value.toString();
2011 }
2012 m_logger->log(u"Failed to parse id"_s, qmlSyntax,
2013 statement->expression->firstSourceLocation());
2014 return QString();
2015 }();
2017 // ### TODO: find an alternative to breakInhertianceCycles here
2018 // we shouldn't need to search for the current root component in any case here
2020 if (auto otherScopeWithID = m_scopesById.scope(name, m_currentScope)) {
2021 auto otherLocation = otherScopeWithID->sourceLocation();
2022 // critical because subsequent analysis cannot cope with messed up ids
2023 // and the file is invalid
2024 m_logger->log(u"Found a duplicated id. id %1 was first declared at %2:%3"_s.arg(
2025 name, QString::number(otherLocation.startLine),
2026 QString::number(otherLocation.startColumn)),
2028 scriptBinding->firstSourceLocation());
2029 }
2030 }
2031 if (!name.isEmpty())
2033}
2034
2043 const QQmlJS::SourceLocation &srcLocation)
2044{
2045 const auto createBinding = [=]() {
2046 const QQmlJSScope::ScopeType type = scope->scopeType();
2052
2053 const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name);
2054 const bool alreadyHasBinding = std::any_of(propertyBindings.first, propertyBindings.second,
2055 [&](const QQmlJSMetaPropertyBinding &binding) {
2056 return binding.bindingType() == bindingType;
2057 });
2058 if (alreadyHasBinding) // no need to create any more
2060
2061 QQmlJSMetaPropertyBinding binding(srcLocation, name);
2063 binding.setGroupBinding(static_cast<QSharedPointer<QQmlJSScope>>(scope));
2064 else
2065 binding.setAttachedBinding(static_cast<QSharedPointer<QQmlJSScope>>(scope));
2066 return binding;
2067 };
2068 return { scope->parentScope(), createBinding };
2069}
2070
2072{
2073 Q_ASSERT(!m_savedBindingOuterScope); // automatically true due to grammar
2074 Q_ASSERT(!m_thisScriptBindingIsJavaScript); // automatically true due to grammar
2076 const auto id = scriptBinding->qualifiedId;
2077 if (!id->next && id->name == QLatin1String("id")) {
2078 handleIdDeclaration(scriptBinding);
2079 return true;
2080 }
2081
2082 auto group = id;
2083
2084 QString prefix;
2085 for (; group->next; group = group->next) {
2086 const QString name = group->name.toString();
2087 if (name.isEmpty())
2088 break;
2089
2090 if (group == id && isImportPrefix(name)) {
2091 prefix = name + u'.';
2092 continue;
2093 }
2094
2095 const bool isAttachedProperty = name.front().isUpper();
2096 if (isAttachedProperty) {
2097 // attached property
2099 group->firstSourceLocation());
2100 } else {
2101 // grouped property
2103 group->firstSourceLocation());
2104 }
2106 group->firstSourceLocation()));
2107
2108 prefix.clear();
2109 }
2110
2111 const auto name = group->name.toString();
2112
2113 // This is a preliminary check.
2114 // Even if the name starts with "on", it might later turn out not to be a signal.
2116
2117 if (!signal.has_value() || m_currentScope->hasProperty(name)) {
2119 { m_savedBindingOuterScope, group->firstSourceLocation(), name });
2120 // ### TODO: report Invalid parse status as a warning/error
2121 auto result = parseBindingExpression(name, scriptBinding->statement);
2123 } else {
2124 const auto statement = scriptBinding->statement;
2125 QStringList signalParameters;
2126
2127 if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
2128 if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
2129 for (FormalParameterList *formal = func->formals; formal; formal = formal->next)
2130 signalParameters << formal->element->bindingIdentifier.toString();
2131 }
2132 }
2133
2134 QQmlJSMetaMethod scopeSignal;
2135 const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal);
2136 if (!methods.isEmpty())
2137 scopeSignal = methods[0];
2138
2139 const auto firstSourceLocation = statement->firstSourceLocation();
2140 bool hasMultilineStatementBody =
2141 statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
2142 m_pendingSignalHandler = firstSourceLocation;
2143 m_signalHandlers.insert(firstSourceLocation,
2144 { scopeSignal.parameterNames(), hasMultilineStatementBody });
2145
2146 // NB: calculate runtime index right away to avoid miscalculation due to
2147 // losing real AST traversal order
2149 const auto createBinding = [
2150 this,
2151 scope = m_currentScope,
2152 signalName = *signal,
2153 index,
2154 name,
2155 firstSourceLocation,
2156 groupLocation = group->firstSourceLocation(),
2157 signalParameters]() {
2158 // when encountering a signal handler, add it as a script binding
2159 Q_ASSERT(scope->isFullyResolved());
2161 const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal);
2162 if (!methods.isEmpty()) {
2164 checkSignal(scope, groupLocation, name, signalParameters);
2165 } else if (QQmlJSUtils::propertyFromChangedHandler(scope, name).has_value()) {
2167 checkSignal(scope, groupLocation, name, signalParameters);
2168 } else if (scope->hasProperty(name)) {
2169 // Not a signal handler after all.
2170 // We can see this now because the type is fully resolved.
2172 m_signalHandlers.remove(firstSourceLocation);
2173 } else {
2174 // We already know it's bad, but let's allow checkSignal() to do its thing.
2175 checkSignal(scope, groupLocation, name, signalParameters);
2176 }
2177
2178 QQmlJSMetaPropertyBinding binding(firstSourceLocation, name);
2179 binding.setScriptBinding(index, kind);
2180 return binding;
2181 };
2184 }
2185
2186 // TODO: before leaving the scopes, we must create the binding.
2187
2188 // Leave any group/attached scopes so that the binding scope doesn't see its properties.
2192 }
2193
2195 scriptBinding->statement->firstSourceLocation());
2196
2197 return true;
2198}
2199
2201{
2205 }
2206
2207 // forgetFunctionExpression() but without the name check since script
2208 // bindings are special (script bindings only sometimes result in java
2209 // script bindings. e.g. a literal binding is also a UiScriptBinding)
2212 Q_ASSERT(!m_functionStack.isEmpty());
2213 m_functionStack.pop();
2214 }
2215}
2216
2218{
2219 enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId),
2220 arrayBinding->firstSourceLocation());
2222
2223 // TODO: support group/attached properties
2224
2225 return true;
2226}
2227
2229{
2230 // immediate children (QML scopes) of m_currentScope are the objects inside
2231 // the array binding. note that we always work with object bindings here as
2232 // this is the only kind of bindings that UiArrayBinding is created for. any
2233 // other expressions involving lists (e.g. `var p: [1,2,3]`) are considered
2234 // to be script bindings
2235 const auto children = m_currentScope->childScopes();
2236 const auto propertyName = getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope);
2238
2239 qsizetype i = 0;
2240 for (auto element = arrayBinding->members; element; element = element->next, ++i) {
2241 const auto &type = children[i];
2242 if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) {
2243 m_logger->log(u"Declaring an object which is not an Qml object"
2244 " as a list member."_s, qmlSyntax, element->firstSourceLocation());
2245 return;
2246 }
2249 element->firstSourceLocation(), false };
2250 QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName);
2255 [binding = std::move(binding)]() { return binding; },
2257 });
2258 }
2259}
2260
2262{
2264 m_logger->log(u"Enums declared inside of inline component are ignored."_s, qmlSyntax,
2265 uied->firstSourceLocation());
2266 }
2267 QQmlJSMetaEnum qmlEnum(uied->name.toString());
2268 qmlEnum.setIsQml(true);
2269 for (const auto *member = uied->members; member; member = member->next) {
2270 qmlEnum.addKey(member->member.toString());
2271 qmlEnum.addValue(int(member->value));
2272 }
2274 return true;
2275}
2276
2277void QQmlJSImportVisitor::addImportWithLocation(const QString &name,
2278 const QQmlJS::SourceLocation &loc)
2279{
2282 return;
2283
2285
2286 // If it's not valid it's a builtin. We don't need to complain about it being unused.
2287 if (loc.isValid())
2289}
2290
2291void QQmlJSImportVisitor::importFromHost(const QString &path, const QString &prefix,
2293{
2294 QFileInfo fileInfo(path);
2295 if (!fileInfo.exists()) {
2296 m_logger->log("File or directory you are trying to import does not exist: %1."_L1.arg(path),
2298 return;
2299 }
2300
2301 if (fileInfo.isFile()) {
2302 const auto scope = m_importer->importFile(path);
2303 const QString actualPrefix = prefix.isEmpty() ? scope->internalName() : prefix;
2304 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
2305 addImportWithLocation(actualPrefix, location);
2306 } else if (fileInfo.isDir()) {
2307 const auto scopes = m_importer->importDirectory(path, prefix);
2309 for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end; it++)
2310 addImportWithLocation(*it, location);
2311 } else {
2312 m_logger->log(
2313 "%1 is neither a file nor a directory. Are sure the import path is correct?"_L1.arg(
2314 path),
2316 }
2317}
2318
2319void QQmlJSImportVisitor::importFromQrc(const QString &path, const QString &prefix,
2321{
2323 if (mapper->isFile(path)) {
2324 const auto entry = m_importer->resourceFileMapper()->entry(
2326 const auto scope = m_importer->importFile(entry.filePath);
2327 const QString actualPrefix =
2328 prefix.isEmpty() ? QFileInfo(entry.resourcePath).baseName() : prefix;
2329 m_rootScopeImports.setType(actualPrefix, { scope, QTypeRevision() });
2330 addImportWithLocation(actualPrefix, location);
2331 } else {
2332 const auto scopes = m_importer->importDirectory(path, prefix);
2334 for (auto it = scopes.types().keyBegin(), end = scopes.types().keyEnd(); it != end; it++)
2335 addImportWithLocation(*it, location);
2336 }
2337 }
2338}
2339
2341{
2342 // construct path
2343 QString prefix = QLatin1String("");
2344 if (import->asToken.isValid()) {
2345 prefix += import->importId;
2346 if (!import->importId.isEmpty() && !import->importId.front().isUpper()) {
2347 m_logger->log(u"Import qualifier '%1' must start with a capital letter."_s.arg(
2348 import->importId),
2349 qmlImport, import->importIdToken, true, true);
2350 }
2351 }
2352
2353 const QString filename = import->fileName.toString();
2354 if (!filename.isEmpty()) {
2355 const QUrl url(filename);
2356 const QString scheme = url.scheme();
2357 const QQmlJS::SourceLocation importLocation = import->firstSourceLocation();
2358 if (scheme == ""_L1) {
2359 QFileInfo fileInfo(url.path());
2360 QString absolute = fileInfo.isRelative()
2361 ? QDir::cleanPath(QDir(m_implicitImportDirectory).filePath(filename))
2362 : filename;
2363 if (absolute.startsWith(u':')) {
2364 importFromQrc(absolute, prefix, importLocation);
2365 } else {
2366 importFromHost(absolute, prefix, importLocation);
2367 }
2368 processImportWarnings("path \"%1\""_L1.arg(url.path()), importLocation);
2369 return true;
2370 } else if (scheme == "file"_L1) {
2371 importFromHost(url.path(), prefix, importLocation);
2372 processImportWarnings("URL \"%1\""_L1.arg(url.path()), importLocation);
2373 return true;
2374 } else if (scheme == "qrc"_L1) {
2375 importFromQrc(":"_L1 + url.path(), prefix, importLocation);
2376 processImportWarnings("URL \"%1\""_L1.arg(url.path()), importLocation);
2377 return true;
2378 } else {
2379 m_logger->log("Unknown import syntax. Imports can be paths, qrc urls or file urls"_L1,
2380 qmlImport, import->firstSourceLocation());
2381 }
2382 }
2383
2384 const QString path = buildName(import->importUri);
2385
2386 QStringList staticModulesProvided;
2387
2388 const auto imported = m_importer->importModule(
2389 path, prefix, import->version ? import->version->version : QTypeRevision(),
2390 &staticModulesProvided);
2391 m_rootScopeImports.addTypes(imported);
2392 for (auto it = imported.types().keyBegin(), end = imported.types().keyEnd(); it != end; it++)
2393 addImportWithLocation(*it, import->firstSourceLocation());
2394
2395 if (prefix.isEmpty()) {
2396 for (const QString &staticModule : staticModulesProvided) {
2397 // Always prefer a direct import of static module to it being imported as a dependency
2398 if (path != staticModule && m_importStaticModuleLocationMap.contains(staticModule))
2399 continue;
2400
2401 m_importStaticModuleLocationMap[staticModule] = import->firstSourceLocation();
2402 }
2403 }
2404
2405 processImportWarnings(QStringLiteral("module \"%1\"").arg(path), import->firstSourceLocation());
2406 return true;
2407}
2408
2409#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
2410template<typename F>
2412{
2413 for (const QQmlJS::AST::UiPragmaValueList *v = pragma->values; v; v = v->next)
2414 assign(v->value);
2415}
2416#else
2417template<typename F>
2418void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
2419{
2420 assign(pragma->value);
2421}
2422#endif
2423
2425{
2426 if (pragma->name == u"Strict"_s) {
2427 // If a file uses pragma Strict, it expects to be compiled, so automatically
2428 // enable compiler warnings unless the level is set explicitly already (e.g.
2429 // by the user).
2430
2432 // TODO: the logic here is rather complicated and may be buggy
2435 }
2436 } else if (pragma->name == u"Singleton") {
2437 m_rootIsSingleton = true;
2438 } else if (pragma->name == u"ComponentBehavior") {
2439 handlePragmaValues(pragma, [this, pragma](QStringView value) {
2440 if (value == u"Bound") {
2442 } else if (value == u"Unbound") {
2444 } else {
2445 m_logger->log(u"Unknown argument \"%1\" to pragma ComponentBehavior"_s.arg(value),
2446 qmlSyntax, pragma->firstSourceLocation());
2447 }
2448 });
2449 } else if (pragma->name == u"FunctionSignatureBehavior") {
2450 handlePragmaValues(pragma, [this, pragma](QStringView value) {
2451 if (value == u"Enforced") {
2453 } else if (value == u"Ignored") {
2455 } else {
2456 m_logger->log(
2457 u"Unknown argument \"%1\" to pragma FunctionSignatureBehavior"_s.arg(value),
2458 qmlSyntax, pragma->firstSourceLocation());
2459 }
2460 });
2461 } else if (pragma->name == u"ValueTypeBehavior") {
2462 handlePragmaValues(pragma, [this, pragma](QStringView value) {
2463 if (value == u"Copy") {
2464 // Ignore
2465 } else if (value == u"Reference") {
2466 // Ignore
2467 } else if (value == u"Addressable") {
2469 } else if (value == u"Inaddressable") {
2471 } else {
2472 m_logger->log(u"Unknown argument \"%1\" to pragma ValueTypeBehavior"_s.arg(value),
2473 qmlSyntax, pragma->firstSourceLocation());
2474 }
2475 });
2476 }
2477
2478 return true;
2479}
2480
2482{
2483 m_logger->log(QStringLiteral("Maximum statement or expression depth exceeded"),
2485}
2486
2493
2498
2505
2510
2517
2522
2533
2538
2545
2550
2552{
2554 catchStatement->firstSourceLocation());
2556 catchStatement->patternElement->bindingIdentifier.toString(),
2557 { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2558 catchStatement->patternElement->firstSourceLocation(), std::nullopt,
2559 catchStatement->patternElement->scope == QQmlJS::AST::VariableScope::Const });
2560 return true;
2561}
2562
2567
2569{
2571 ast->firstSourceLocation());
2572
2573 m_logger->log(QStringLiteral("with statements are strongly discouraged in QML "
2574 "and might cause false positives when analysing unqualified "
2575 "identifiers"),
2577
2578 return true;
2579}
2580
2585
2587{
2588 while (vdl) {
2589 std::optional<QString> typeName;
2590 if (TypeAnnotation *annotation = vdl->declaration->typeAnnotation)
2591 if (Type *type = annotation->type)
2592 typeName = type->toString();
2593
2595 vdl->declaration->bindingIdentifier.toString(),
2596 { (vdl->declaration->scope == QQmlJS::AST::VariableScope::Var)
2597 ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
2598 : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
2599 vdl->declaration->firstSourceLocation(), typeName,
2600 vdl->declaration->scope == QQmlJS::AST::VariableScope::Const });
2601 vdl = vdl->next;
2602 }
2603 return true;
2604}
2605
2607{
2608 for (auto const &boundName : fpl->boundNames()) {
2609
2610 std::optional<QString> typeName;
2611 if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
2612 if (Type *type = annotation->type)
2613 typeName = type->toString();
2614 m_currentScope->insertJSIdentifier(boundName.id,
2616 boundName.location, typeName, false });
2617 }
2618 return true;
2619}
2620
2622{
2623 // ... __styleData: QtObject {...}
2624
2625 Q_ASSERT(uiob->qualifiedTypeNameId);
2626
2627 bool needsResolution = false;
2628 int scopesEnteredCounter = 0;
2629
2630 const QString typeName = buildName(uiob->qualifiedTypeNameId);
2631 if (typeName.front().isLower() && typeName.contains(u'.')) {
2632 logLowerCaseImport(typeName, uiob->qualifiedTypeNameId->identifierToken, m_logger);
2633 }
2634
2635 QString prefix;
2636 for (auto group = uiob->qualifiedId; group->next; group = group->next) {
2637 const QString idName = group->name.toString();
2638
2639 if (idName.isEmpty())
2640 break;
2641
2642 if (group == uiob->qualifiedId && isImportPrefix(idName)) {
2643 prefix = idName + u'.';
2644 continue;
2645 }
2646
2647 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
2649
2650 bool exists =
2651 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
2652
2654 group->firstSourceLocation()));
2655
2656 ++scopesEnteredCounter;
2657 needsResolution = needsResolution || !exists;
2658
2659 prefix.clear();
2660 }
2661
2662 for (int i=0; i < scopesEnteredCounter; ++i) { // leave the scopes we entered again
2664 }
2665
2666 // recursively resolve types for current scope if new scopes are found
2667 if (needsResolution)
2669
2671 uiob->qualifiedTypeNameId->identifierToken);
2673
2674 m_qmlTypes.append(m_currentScope); // new QMLScope is created here, so add it
2676 return true;
2677}
2678
2680{
2682 // must be mutable, as we might mark it as implicitly wrapped in a component
2683 const QQmlJSScope::Ptr childScope = m_currentScope;
2685
2686 auto group = uiob->qualifiedId;
2687 int scopesEnteredCounter = 0;
2688
2689 QString prefix;
2690 for (; group->next; group = group->next) {
2691 const QString idName = group->name.toString();
2692
2693 if (idName.isEmpty())
2694 break;
2695
2696 if (group == uiob->qualifiedId && isImportPrefix(idName)) {
2697 prefix = idName + u'.';
2698 continue;
2699 }
2700
2701 const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope
2703 // definitely exists
2704 [[maybe_unused]] bool exists =
2705 enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation());
2706 Q_ASSERT(exists);
2707 scopesEnteredCounter++;
2708
2709 prefix.clear();
2710 }
2711
2712 // on ending the visit to UiObjectBinding, set the property type to the
2713 // just-visited one if the property exists and this type is valid
2714
2715 const QString propertyName = group->name.toString();
2716
2717 if (m_currentScope->isNameDeferred(propertyName)) {
2718 bool foundIds = false;
2719 QList<QQmlJSScope::ConstPtr> childScopes { childScope };
2720
2721 while (!childScopes.isEmpty()) {
2722 const QQmlJSScope::ConstPtr scope = childScopes.takeFirst();
2723 if (!m_scopesById.id(scope, scope).isEmpty()) {
2724 foundIds = true;
2725 break;
2726 }
2727
2728 childScopes << scope->childScopes();
2729 }
2730
2731 if (foundIds) {
2732 m_logger->log(
2733 u"Cannot defer property assignment to \"%1\". Assigning an id to an object or one of its sub-objects bound to a deferred property will make the assignment immediate."_s
2734 .arg(propertyName),
2735 qmlDeferredPropertyId, uiob->firstSourceLocation());
2736 }
2737 }
2738
2740 // These warnings do not apply for custom parsers and their children and need to be handled
2741 // on a case by case basis
2742 } else {
2744 << PendingPropertyObjectBinding { m_currentScope, childScope, propertyName,
2745 uiob->firstSourceLocation(), uiob->hasOnToken };
2746
2747 QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName);
2748 if (uiob->hasOnToken) {
2749 if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) {
2751 QQmlJSScope::ConstPtr(childScope));
2752 } else { // if (childScope->hasInterface(u"QQmlPropertyValueSource"_s))
2754 QQmlJSScope::ConstPtr(childScope));
2755 }
2756 } else {
2758 QQmlJSScope::ConstPtr(childScope));
2759 }
2760 m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; } });
2761 }
2762
2763 for (int i = 0; i < scopesEnteredCounter; ++i)
2765}
2766
2775
2782
2784{
2786 enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("module"),
2787 module->firstSourceLocation());
2789 importBaseModules();
2791 return true;
2792}
2793
2798
2800{
2806 importBaseModules();
2807 return true;
2808}
2809
2814
2816{
2817 // This is a rather rough approximation of "used type" but the "unused import"
2818 // info message doesn't have to be 100% accurate.
2819 const QString name = fieldMember->name.toString();
2822 if (type.scope.isNull()) {
2825 } else if (!type.scope->ownAttachedTypeName().isEmpty()) {
2827 }
2828 }
2829}
2830
2832{
2833 const QString name = idexp->name.toString();
2836 }
2837
2838 return true;
2839}
2840
2842{
2843 // Handles variable declarations such as var x = [1,2,3].
2844 if (element->isVariableDeclaration()) {
2846 element->boundNames(&names);
2847 for (const auto &name : names) {
2848 std::optional<QString> typeName;
2849 if (TypeAnnotation *annotation = name.typeAnnotation.data())
2850 if (Type *type = annotation->type)
2851 typeName = type->toString();
2853 name.id,
2857 name.location, typeName,
2859 }
2860 }
2861
2862 return true;
2863}
2864
static JNINativeMethod methods[]
Definition lalr.h:136
\inmodule QtCore
\inmodule QtCore
Definition qdir.h:20
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
QString baseName() const
Returns the base name of the file without the path.
QString absoluteFilePath() const
QString canonicalPath() const
Returns the file system entry's canonical path (excluding the entry's name), i.e.
bool isRelative() const
Returns true if the file system entry's path is relative, otherwise returns false (that is,...
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:927
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition qhash.h:1762
bool contains(const Key &key) const noexcept
Definition qhash.h:1658
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:2034
void setFilename(const QString &filename)
void setAutoApplicable(bool autoApply=true)
QStack< FunctionOrExpressionIdentifier > m_functionStack
void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope)
QHash< QQmlJSScope::ConstPtr, QList< QString > > m_functionsAndExpressions
QMultiHash< QString, QQmlJS::SourceLocation > m_importTypeLocationMap
QQmlJSScope::Ptr m_currentScope
QMultiHash< QString, QQmlJS::SourceLocation > m_importStaticModuleLocationMap
QQmlJS::SourceLocation m_pendingSignalHandler
QQmlJSScope::ConstPtr m_globalScope
void breakInheritanceCycles(const QQmlJSScope::Ptr &scope)
QList< QQmlJSScope::ConstPtr > m_qmlTypes
void checkDeprecation(const QQmlJSScope::ConstPtr &scope)
QQmlJSScope::Ptr m_savedBindingOuterScope
static QString implicitImportDirectory(const QString &localFile, QQmlJSResourceFileMapper *mapper)
void populateRuntimeFunctionIndicesForDocument() const
QHash< QQmlJS::SourceLocation, QQmlJSMetaSignalHandler > m_signalHandlers
QHash< QV4::CompiledData::Location, QQmlJSScope::ConstPtr > m_scopesByIrLocation
QList< UnfinishedBinding > m_bindings
QHash< QQmlJSScope::Ptr, QVector< QQmlJSScope::Ptr > > m_pendingDefaultProperties
QVector< QQmlJSScope::Ptr > m_objectDefinitionScopes
QVector< QQmlJSAnnotation > m_pendingMethodAnnotations
QQmlJSScope::RootDocumentNameType RootDocumentNameType
void endVisit(QQmlJS::AST::ExpressionStatement *ast) override
bool enterEnvironmentNonUnique(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location)
void throwRecursionDepthError() override
void forgetFunctionExpression(const QString &name)
QVector< PendingMethodType > m_pendingMethodTypes
QHash< FunctionOrExpressionIdentifier, int > m_innerFunctions
QQmlJSMetaMethod::RelativeFunctionIndex addFunctionOrExpression(const QQmlJSScope::ConstPtr &scope, const QString &name)
bool isImportPrefix(QString prefix) const
QVector< QQmlJSAnnotation > parseAnnotations(QQmlJS::AST::UiAnnotationList *list)
QVector< PendingPropertyObjectBinding > m_pendingPropertyObjectBindings
QSet< QQmlJS::SourceLocation > m_importLocations
QVector< RequiredProperty > m_requiredProperties
QVector< PendingPropertyType > m_pendingPropertyTypes
int synthesizeCompilationUnitRuntimeFunctionIndices(const QQmlJSScope::Ptr &scope, int count) const
QVector< QQmlJSScope::Ptr > m_objectBindingScopes
QSet< QQmlJSScope::ConstPtr > m_literalScopesToCheck
bool isTypeResolved(const QQmlJSScope::ConstPtr &type, ErrorHandler handle)
void enterEnvironment(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location)
bool visit(QQmlJS::AST::StringLiteral *) override
const QQmlJSScope::Ptr m_exportedRootScope
BindingExpressionParseResult parseBindingExpression(const QString &name, const QQmlJS::AST::Statement *statement)
QQmlJSImporter::ImportedTypes m_rootScopeImports
QHash< QQmlJSScope::Ptr, QVector< WithVisibilityScope< QString > > > m_propertyBindings
QQmlJSScopesById m_scopesById
QQmlJSScope::InlineComponentOrDocumentRootName m_currentRootName
void importQmldirs(const QStringList &qmltypesFiles)
Imports types from the specified qmltypesFiles.
ImportedTypes builtinInternalNames()
QQmlJSScope::Ptr importFile(const QString &file)
ImportedTypes importBuiltins()
Imports builtins.qmltypes and jsroot.qmltypes found in any of the import paths.
QList< QQmlJS::DiagnosticMessage > takeWarnings()
ImportedTypes importDirectory(const QString &directory, const QString &prefix=QString())
QQmlJSResourceFileMapper * resourceFileMapper() const
ImportedTypes importModule(const QString &module, const QString &prefix=QString(), QTypeRevision version=QTypeRevision(), QStringList *staticModuleList=nullptr)
void processMessages(const QList< QQmlJS::DiagnosticMessage > &messages, const QQmlJS::LoggerWarningId id)
QString code() const
QString fileName() const
bool wasCategoryChanged(QQmlJS::LoggerWarningId id) const
void setCategoryIgnored(QQmlJS::LoggerWarningId id, bool error)
void setCategoryLevel(QQmlJS::LoggerWarningId id, QtMsgType level)
void log(const QString &message, QQmlJS::LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation, bool showContext=true, bool showFileName=true, const std::optional< QQmlJSFixSuggestion > &suggestion={}, const QString overrideFileName=QString())
void setIsQml(bool v)
void addValue(int value)
void addKey(const QString &key)
void setStringLiteral(QAnyStringView value)
void setScriptBinding(QQmlJSMetaMethod::RelativeFunctionIndex value, ScriptBindingKind kind, ScriptBindingValueType valueType=ScriptBindingValueType::ScriptValue_Unknown)
void setRegexpLiteral(QAnyStringView value)
void setInterceptor(const QString &typeName, const QSharedPointer< const QQmlJSScope > &type)
void setTranslationId(QStringView id, int number)
void setObject(const QString &typeName, const QSharedPointer< const QQmlJSScope > &type)
void setGroupBinding(const QSharedPointer< const QQmlJSScope > &groupScope)
BindingType bindingType() const
void setNumberLiteral(double value)
void setAttachedBinding(const QSharedPointer< const QQmlJSScope > &attachingScope)
void setTranslation(QStringView text, QStringView comment, QStringView context, int number)
void setValueSource(const QString &typeName, const QSharedPointer< const QQmlJSScope > &type)
void setPropertyName(const QString &propertyName)
void setAnnotations(const QList< QQmlJSAnnotation > &annotation)
void setIsList(bool isList)
QSharedPointer< const QQmlJSScope > type() const
QString typeName() const
void setTypeName(const QString &typeName)
void setIsWritable(bool isWritable)
void setAliasExpression(const QString &aliasString)
void setIndex(int index)
void setType(const QSharedPointer< const QQmlJSScope > &type)
QString propertyName() const
void setIsPointer(bool isPointer)
Tracks the types for the QmlCompiler.
void setIsInlineComponent(bool v)
bool isComposite() const
void setIsComposite(bool v)
void insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
bool isNameDeferred(const QString &name) const
void setIsScript(bool v)
QHash< QString, QQmlJSMetaMethod > methods() const
Returns all methods visible from this scope including those of base types and extensions.
QString defaultPropertyName() const
bool isInCustomParserParent() const
void setAnnotations(const QList< QQmlJSAnnotation > &annotation)
static QQmlJSScope::Ptr create()
const QList< QQmlJSAnnotation > & annotations() const
QQmlJSScope::Ptr parentScope()
ScopeType scopeType() const
void setInternalName(const QString &internalName)
QString baseTypeName() const
QString internalName() const
static ImportedScope< QQmlJSScope::ConstPtr > findType(const QString &name, const QQmlJS::ContextualTypes &contextualTypes, QSet< QString > *usedTypes=nullptr)
void setIsSingleton(bool v)
void setScopeType(ScopeType type)
void addOwnProperty(const QQmlJSMetaProperty &prop)
static QTypeRevision resolveTypes(const Ptr &self, const QQmlJS::ContextualTypes &contextualTypes, QSet< QString > *usedTypes=nullptr)
QString moduleName() const
bool isInlineComponent() const
void setOwnModuleName(const QString &moduleName)
void insertPropertyIdentifier(const QQmlJSMetaProperty &prop)
void setIsArrayScope(bool v)
QVector< QQmlJSScope::Ptr > childScopes()
void setBaseTypeName(const QString &baseTypeName)
bool isFullyResolved() const
bool hasInterface(const QString &name) const
void setSourceLocation(const QQmlJS::SourceLocation &sourceLocation)
void setInlineComponentName(const QString &inlineComponentName)
static QQmlJSScope::ConstPtr nonCompositeBaseType(const QQmlJSScope::ConstPtr &type)
static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin)
void addOwnEnumeration(const QQmlJSMetaEnum &enumeration)
bool isArrayScope() const
QDeferredSharedPointer< const QQmlJSScope > ConstPtr
QQmlJSMetaProperty property(const QString &name) const
QQmlJSScope::ConstPtr baseType() const
void addOwnMethod(const QQmlJSMetaMethod &method)
std::optional< QString > inlineComponentName() const
QHash< QString, QQmlJSMetaProperty > ownProperties() const
void addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index)
void setOwnDefaultPropertyName(const QString &name)
bool hasProperty(const QString &name) const
static void reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope)
QMultiHash< QString, QQmlJSMetaPropertyBinding > ownPropertyBindings() const
QQmlJS::SourceLocation sourceLocation() const
QString baseTypeError() const
static void resolveGeneralizedGroup(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &baseType, const QQmlJS::ContextualTypes &contextualTypes, QSet< QString > *usedTypes=nullptr)
void setFilePath(const QString &file)
QMultiHash< QString, QQmlJSMetaMethod > ownMethods() const
void setPropertyLocallyRequired(const QString &name, bool isRequired)
bool signaturesAreEnforced() const
bool existsAnywhereInDocument(const QString &id) const
void setValueTypesAreAddressable(bool addressable)
void setComponentsAreBound(bool bound)
QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer, QQmlJSScopesByIdOptions options=Default) const
void setSignaturesAreEnforced(bool enforced)
QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer, QQmlJSScopesByIdOptions options=Default) const
void insert(const QString &id, const QQmlJSScope::ConstPtr &scope)
SourceLocation firstSourceLocation() const override
SourceLocation firstSourceLocation() const override
SourceLocation firstSourceLocation() const override
SourceLocation firstSourceLocation() const override
SourceLocation firstSourceLocation() const override
SourceLocation firstSourceLocation() const override
SourceLocation firstSourceLocation() const override
FunctionExpression * asFunctionDefinition() override
virtual SourceLocation firstSourceLocation() const =0
virtual void boundNames(BoundNames *names)
bool isVariableDeclaration() const
UiQualifiedId * qualifiedTypeNameId
UiObjectInitializer * initializer
SourceLocation firstSourceLocation() const override
SourceLocation firstSourceLocation() const override
static std::optional< QString > handlerNameToSignalName(QStringView handler)
qsizetype size() const
Definition qset.h:50
bool remove(const T &value)
Definition qset.h:63
iterator end()
Definition qset.h:140
const_iterator constEnd() const noexcept
Definition qset.h:143
const_iterator constFind(const T &value) const
Definition qset.h:161
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView first(qsizetype n) const noexcept
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1121
constexpr QChar at(qsizetype n) const noexcept
Returns the character at position n in this string view.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
bool isNull() const
Returns true if this string is null; otherwise returns false.
Definition qstring.h:994
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
QChar front() const
Definition qstring.h:230
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
QString & append(QChar c)
Definition qstring.cpp:3252
\inmodule QtCore
\inmodule QtCore
Definition qurl.h:94
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1991
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2468
static const char * s_globalNames[]
QSet< QString >::iterator it
auto signal
Combined button and popup list for selecting options.
void tryGeneratingTranslationBindingBase(QStringView base, QQmlJS::AST::ArgumentList *args, RegisterMainString registerMainString, RegisterCommentString registerCommentString, RegisterContextString registerContextString, FinalizeTranslationData finalizeTranslationData)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
static const QCssKnownValue properties[NumProperties - 1]
DBusConnection const char DBusError * error
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall * pending
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
@ QtWarningMsg
Definition qlogging.h:31
const char * typeName
GLint location
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLint GLenum GLint components
GLuint index
[2]
GLuint GLuint end
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLuint object
[3]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLboolean GLuint group
GLenum target
GLuint GLsizei const GLchar * message
GLenum const GLint * param
GLuint name
GLsizei GLenum const void * indices
GLdouble s
[6]
Definition qopenglext.h:235
GLenum func
Definition qopenglext.h:663
const GLubyte * c
GLuint segment
GLuint GLuint * names
GLuint entry
GLenum array
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
QT_BEGIN_NAMESPACE constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
static qreal component(const QPointF &point, unsigned int i)
static QQmlAnyBinding createBinding(const QQmlProperty &prop, const QV4::CompiledData::Binding *binding, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QQmlRefPointer< QQmlContextData > &contextData, QObject *scopeObject)
Definition qqmlbind.cpp:710
static QString internalName(const QQmlJSScope::ConstPtr &scope)
void handleTranslationBinding(QQmlJSMetaPropertyBinding &binding, QStringView base, QQmlJS::AST::ArgumentList *args)
void setScopeName(QQmlJSScope::Ptr &scope, QQmlJSScope::ScopeType type, const QString &name)
QString getScopeName(const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ScopeType type)
static bool causesImplicitComponentWrapping(const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &assignedType)
void handlePragmaValues(QQmlJS::AST::UiPragma *pragma, F &&assign)
static void logLowerCaseImport(QStringView superType, QQmlJS::SourceLocation location, QQmlJSLogger *logger)
static bool mayBeUnresolvedGeneralizedGroupedProperty(const QQmlJSScope::ConstPtr &scope)
QQmlJSImportVisitor::UnfinishedBinding createNonUniqueScopeBinding(QQmlJSScope::Ptr &scope, const QString &name, const QQmlJS::SourceLocation &srcLocation)
QString buildName(const Node *node)
static QQmlJSAnnotation::Value bindingToVariant(QQmlJS::AST::Statement *statement)
const QQmlSA::LoggerWarningId qmlAliasCycle
const QQmlSA::LoggerWarningId qmlSignalParameters
const QQmlSA::LoggerWarningId qmlUnresolvedAlias
const QQmlSA::LoggerWarningId qmlDuplicatePropertyBinding
const QQmlSA::LoggerWarningId qmlRecursionDepthErrors
const QQmlSA::LoggerWarningId qmlSyntaxIdQuotation
const QQmlSA::LoggerWarningId qmlDuplicatedName
const QQmlSA::LoggerWarningId qmlUncreatableType
const QQmlSA::LoggerWarningId qmlMissingProperty
const QQmlSA::LoggerWarningId qmlMultilineStrings
const QQmlSA::LoggerWarningId qmlSyntaxDuplicateIds
const QQmlSA::LoggerWarningId qmlUnqualified
const QQmlSA::LoggerWarningId qmlRequired
const QQmlSA::LoggerWarningId qmlTopLevelComponent
const QQmlSA::LoggerWarningId qmlDeferredPropertyId
const QQmlSA::LoggerWarningId qmlWith
const QQmlSA::LoggerWarningId qmlUnresolvedType
const QQmlSA::LoggerWarningId qmlImport
const QQmlSA::LoggerWarningId qmlMissingType
const QQmlSA::LoggerWarningId qmlInheritanceCycle
const QQmlSA::LoggerWarningId qmlIncompatibleType
const QQmlSA::LoggerWarningId qmlUnusedImports
const QQmlSA::LoggerWarningId qmlDeprecated
const QQmlSA::LoggerWarningId qmlCompiler
const QQmlSA::LoggerWarningId qmlSyntax
const QQmlSA::LoggerWarningId qmlNonListProperty
@ ScriptValue_Unknown
@ ScriptValue_Undefined
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
static const uint base
Definition qurlidna.cpp:20
const char property[13]
Definition qwizard.cpp:101
QList< int > list
[14]
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QGraphicsItem * item
QDataWidgetMapper * mapper
[0]
view create()
QJSValueList args
\inmodule QtCore \reentrant
Definition qchar.h:18
bool contains(const AT &t) const noexcept
Definition qlist.h:45
std::variant< QString, double > Value
Entry entry(const Filter &filter) const
static Filter resourceFileFilter(const QString &file)
static Filter localFileFilter(const QString &file)
static void traverseFollowingQmlIrObjectStructure(const QQmlJSScope::Ptr &root, Action act)
static std::optional< QQmlJSMetaProperty > propertyFromChangedHandler(const QQmlJSScope::ConstPtr &scope, QStringView changedHandler)
static std::optional< QQmlJSFixSuggestion > didYouMean(const QString &userInput, QStringList candidates, QQmlJS::SourceLocation location)
const QHash< QString, ImportedScope< QQmlJSScope::ConstPtr > > & types() const
void addTypes(ContextualTypes &&types)
bool isNullType(const QString &name) const
ImportedScope< QQmlJSScope::ConstPtr > type(const QString &name) const
bool hasType(const QString &name) const
QQmlJSScope::ConstPtr arrayType() const
void setType(const QString &name, const ImportedScope< QQmlJSScope::ConstPtr > &type)