// Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef QQMLJSIMPORTEDMEMBERSVISITOR_P_H #define QQMLJSIMPORTEDMEMBERSVISITOR_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QQmlJS::Dom { class QQmlDomAstCreatorWithQQmlJSScope; } struct QQmlJSResourceFileMapper; class Q_QMLCOMPILER_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor { public: QQmlJSImportVisitor(); QQmlJSImportVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger, const QString &implicitImportDirectory, const QStringList &qmldirFiles = QStringList()); ~QQmlJSImportVisitor(); using QQmlJS::AST::Visitor::endVisit; using QQmlJS::AST::Visitor::postVisit; using QQmlJS::AST::Visitor::preVisit; using QQmlJS::AST::Visitor::visit; QQmlJSScope::Ptr result() const { return m_exportedRootScope; } const QQmlJSLogger *logger() const { return m_logger; } QQmlJSLogger *logger() { return m_logger; } QQmlJSImporter::ImportedTypes imports() const { return m_rootScopeImports; } QQmlJSScopesById addressableScopes() const { return m_scopesById; } QHash signalHandlers() const { return m_signalHandlers; } QSet literalScopesToCheck() const { return m_literalScopesToCheck; } QList qmlTypes() const { return m_qmlTypes; } QHash scopesBylocation() const { return m_scopesByIrLocation; } static QString implicitImportDirectory( const QString &localFile, QQmlJSResourceFileMapper *mapper); // ### should this be restricted? QQmlJSImporter *importer() { return m_importer; } const QQmlJSImporter *importer() const { return m_importer; } struct UnfinishedBinding { QQmlJSScope::Ptr owner; std::function create; QQmlJSScope::BindingTargetSpecifier specifier = QQmlJSScope::SimplePropertyTarget; }; QStringList seenModuleQualifiers() const { return m_seenModuleQualifiers; } protected: // Linter warnings, we might want to move this at some point bool visit(QQmlJS::AST::StringLiteral *) override; bool visit(QQmlJS::AST::ExpressionStatement *ast) override; void endVisit(QQmlJS::AST::ExpressionStatement *ast) override; bool visit(QQmlJS::AST::UiProgram *) override; void endVisit(QQmlJS::AST::UiProgram *) override; bool visit(QQmlJS::AST::UiObjectDefinition *) override; void endVisit(QQmlJS::AST::UiObjectDefinition *) override; bool visit(QQmlJS::AST::UiInlineComponent *) override; void endVisit(QQmlJS::AST::UiInlineComponent *) override; bool visit(QQmlJS::AST::UiPublicMember *) override; void endVisit(QQmlJS::AST::UiPublicMember *) override; bool visit(QQmlJS::AST::UiRequired *required) override; bool visit(QQmlJS::AST::UiScriptBinding *) override; void endVisit(QQmlJS::AST::UiScriptBinding *) override; bool visit(QQmlJS::AST::UiArrayBinding *) override; void endVisit(QQmlJS::AST::UiArrayBinding *) override; bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override; void handleDuplicateEnums(QQmlJS::AST::UiEnumMemberList *members, const QString &key, const QQmlJS::SourceLocation &location); bool visit(QQmlJS::AST::FunctionExpression *fexpr) override; void endVisit(QQmlJS::AST::FunctionExpression *) override; bool visit(QQmlJS::AST::UiSourceElement *) override; bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override; void endVisit(QQmlJS::AST::FunctionDeclaration *) override; bool visit(QQmlJS::AST::ClassExpression *ast) override; void endVisit(QQmlJS::AST::ClassExpression *) override; bool visit(QQmlJS::AST::UiImport *import) override; bool visit(QQmlJS::AST::UiPragma *pragma) override; bool visit(QQmlJS::AST::ClassDeclaration *ast) override; void endVisit(QQmlJS::AST::ClassDeclaration *ast) override; bool visit(QQmlJS::AST::ForStatement *ast) override; void endVisit(QQmlJS::AST::ForStatement *ast) override; bool visit(QQmlJS::AST::ForEachStatement *ast) override; void endVisit(QQmlJS::AST::ForEachStatement *ast) override; bool visit(QQmlJS::AST::Block *ast) override; void endVisit(QQmlJS::AST::Block *ast) override; bool visit(QQmlJS::AST::CaseBlock *ast) override; void endVisit(QQmlJS::AST::CaseBlock *ast) override; bool visit(QQmlJS::AST::Catch *ast) override; void endVisit(QQmlJS::AST::Catch *ast) override; bool visit(QQmlJS::AST::WithStatement *withStatement) override; void endVisit(QQmlJS::AST::WithStatement *ast) override; bool visit(QQmlJS::AST::VariableDeclarationList *vdl) override; bool visit(QQmlJS::AST::FormalParameterList *fpl) override; bool visit(QQmlJS::AST::UiObjectBinding *uiob) override; void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override; bool visit(QQmlJS::AST::ExportDeclaration *exp) override; void endVisit(QQmlJS::AST::ExportDeclaration *exp) override; bool visit(QQmlJS::AST::ESModule *module) override; void endVisit(QQmlJS::AST::ESModule *module) override; bool visit(QQmlJS::AST::Program *program) override; void endVisit(QQmlJS::AST::Program *program) override; void endVisit(QQmlJS::AST::FieldMemberExpression *) override; bool visit(QQmlJS::AST::IdentifierExpression *idexp) override; bool visit(QQmlJS::AST::PatternElement *) override; bool visit(QQmlJS::AST::IfStatement *) override; void throwRecursionDepthError() override; virtual bool checkCustomParser(const QQmlJSScope::ConstPtr &scope); QString m_implicitImportDirectory; QStringList m_qmldirFiles; QQmlJSScope::Ptr m_currentScope; const QQmlJSScope::Ptr m_exportedRootScope; QQmlJSImporter *m_importer = nullptr; QQmlJSLogger *m_logger = nullptr; using RootDocumentNameType = QQmlJSScope::RootDocumentNameType; using InlineComponentNameType = QQmlJSScope::InlineComponentNameType; using InlineComponentOrDocumentRootName = QQmlJSScope::RootDocumentNameType; QQmlJSScope::InlineComponentOrDocumentRootName m_currentRootName = QQmlJSScope::RootDocumentNameType(); bool m_nextIsInlineComponent = false; bool m_rootIsSingleton = false; QQmlJSScope::Ptr m_savedBindingOuterScope; QQmlJSScope::ConstPtr m_globalScope; QQmlJSScopesById m_scopesById; QQmlJSImporter::ImportedTypes m_rootScopeImports; QList m_qmlTypes; // We need to record the locations as IR locations because those contain less data. // This way we can look up objects by IR location later. QHash m_scopesByIrLocation; // Maps all qmlNames to the source location of their import QMultiHash m_importTypeLocationMap; // Maps all static modules to the source location of their import QMultiHash m_importStaticModuleLocationMap; // Contains all import source locations (could be extracted from above but that is expensive) QSet m_importLocations; // A set of all types that have been used during type resolution QSet m_usedTypes; QList m_bindings; // stores JS functions and Script bindings per scope (only the name). mimics // the content of QmlIR::Object::functionsAndExpressions QHash> m_functionsAndExpressions; template struct ScopeAndNameT { using Scope = std::conditional_t; ScopeAndNameT() = default; ScopeAndNameT(const Scope &scope, const QString &name) : scope(scope), name(name) { } ScopeAndNameT(const ScopeAndNameT &) = default; ScopeAndNameT(ScopeAndNameT &&) = default; ScopeAndNameT &operator=(const ScopeAndNameT &) = default; ScopeAndNameT &operator=(ScopeAndNameT &&) = default; ~ScopeAndNameT() = default; // Create const from non-const ScopeAndNameT(typename std::enable_if>::type &nonConst) : scope(nonConst.scope), name(nonConst.name) { } friend bool operator==(const ScopeAndNameT &lhs, const ScopeAndNameT &rhs) { return lhs.scope == rhs.scope && lhs.name == rhs.name; } friend bool operator!=(const ScopeAndNameT &lhs, const ScopeAndNameT &rhs) { return !(lhs == rhs); } friend size_t qHash(const ScopeAndNameT &san, size_t seed = 0) { return qHashMulti(seed, san.scope, san.name); } Scope scope; QString name; }; using ConstScopeAndName = ScopeAndNameT; using ScopeAndName = ScopeAndNameT; using FunctionOrExpressionIdentifier = ConstScopeAndName; using Property = ConstScopeAndName; using Alias = ConstScopeAndName; // tells whether last-processed UiScriptBinding is truly a script binding bool m_thisScriptBindingIsJavaScript = false; QStack m_functionStack; // stores the number of functions inside each function QHash m_innerFunctions; QQmlJSMetaMethod::RelativeFunctionIndex addFunctionOrExpression(const QQmlJSScope::ConstPtr &scope, const QString &name); void forgetFunctionExpression(const QString &name); int synthesizeCompilationUnitRuntimeFunctionIndices(const QQmlJSScope::Ptr &scope, int count) const; void populateRuntimeFunctionIndicesForDocument() const; void enterEnvironment(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location); // Finds an existing scope before attempting to create a new one. Returns \c // true if the scope already exists and \c false if the new scope is created bool enterEnvironmentNonUnique(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location); void leaveEnvironment(); // A set of types that have not been resolved but have been used during the // AST traversal QDuplicateTracker m_unresolvedTypes; template bool checkTypeResolved(const QQmlJSScope::ConstPtr &type, ErrorHandler handle) { if (type->isFullyResolved() || checkCustomParser(type)) return true; // Note: ignore duplicates, but only after we are certain that the type // is still unresolved if (!m_unresolvedTypes.hasSeen(type)) handle(type); return false; } bool checkTypeResolved(const QQmlJSScope::ConstPtr &type) { return checkTypeResolved(type, [&](const QQmlJSScope::ConstPtr &type) { warnUnresolvedType(type); }); } void warnUnresolvedType(const QQmlJSScope::ConstPtr &type) const; void warnMissingPropertyForBinding( const QString &property, const QQmlJS::SourceLocation &location, const std::optional &fixSuggestion = {}); QVector parseAnnotations(QQmlJS::AST::UiAnnotationList *list); void setAllBindings(); void addDefaultProperties(); void processDefaultProperties(); void processPropertyBindings(); void checkRequiredProperties(); void processPropertyTypes(); void processMethodTypes(); void processPropertyBindingObjects(); void flushPendingSignalParameters(); QQmlJSScope::ConstPtr scopeById(const QString &id, const QQmlJSScope::ConstPtr ¤t); void breakInheritanceCycles(const QQmlJSScope::Ptr &scope); void checkDeprecation(const QQmlJSScope::ConstPtr &scope); void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope); bool rootScopeIsValid() const { return m_exportedRootScope->sourceLocation().isValid(); } enum class BindingExpressionParseResult { Invalid, Script, Literal, Translation }; BindingExpressionParseResult parseBindingExpression(const QString &name, const QQmlJS::AST::Statement *statement); bool isImportPrefix(QString prefix) const; // Used to temporarily store annotations for functions and generators wrapped in UiSourceElements QVector m_pendingMethodAnnotations; struct PendingPropertyType { QQmlJSScope::Ptr scope; QString name; QQmlJS::SourceLocation location; }; struct PendingMethodTypeAnnotations { QQmlJSScope::Ptr scope; QString methodName; // This keeps type annotations' locations in order (parameters then return type). // If an annotation is not present, it is represented by an invalid source location. QVarLengthArray locations; }; struct PendingPropertyObjectBinding { QQmlJSScope::Ptr scope; QQmlJSScope::Ptr childScope; QString name; QQmlJS::SourceLocation location; bool onToken; }; struct RequiredProperty { QQmlJSScope::Ptr scope; QString name; QQmlJS::SourceLocation location; }; /*! Utility wrapper that adds visibility scope to the data. This wrapper becomes useful for binding processing where we need to know both the property (or signal handler) owner and the scope in which the binding is executed (the "visibility" scope). As visibility scope (and data) does not typically have sufficient information about a proper source location of that data, the location also has to be provided to simplify the error reporting. */ template struct WithVisibilityScope { QQmlJSScope::Ptr visibilityScope; QQmlJS::SourceLocation dataLocation; T data; }; QHash> m_pendingDefaultProperties; QVector m_pendingPropertyTypes; QVector m_pendingMethodTypeAnnotations; QVector m_pendingPropertyObjectBindings; QVector m_requiredProperties; QVector m_objectBindingScopes; QVector m_objectDefinitionScopes; QHash>> m_propertyBindings; QVector m_aliasDefinitions; QHash> m_propertyAliases; QHash m_signalHandlers; QSet m_literalScopesToCheck; QQmlJS::SourceLocation m_pendingSignalHandler; QStringList m_seenModuleQualifiers; QHash m_seenInlineComponents; private: void registerTargetIntoImporter(const QQmlJSScope::Ptr &target); void checkSignal( const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location, const QString &handlerName, const QStringList &handlerParameters); void importBaseModules(); void resolveAliases(); void populatePropertyAliases(); void resolveGroupProperties(); void handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding); void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr); void processImportWarnings( const QString &what, const QList &warnings, const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation()); void addImportWithLocation( const QString &name, const QQmlJS::SourceLocation &loc, bool hadWarnings); void populateCurrentScope(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location); void enterRootScope(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location); QList importFromHost( const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location); QList importFromQrc( const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location); public: friend class QQmlJS::Dom::QQmlDomAstCreatorWithQQmlJSScope; }; QT_END_NAMESPACE #endif // QQMLJSIMPORTEDMEMBERSVISITOR_P_H