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
4#ifndef QQMLJSIMPORTEDMEMBERSVISITOR_P_H
5#define QQMLJSIMPORTEDMEMBERSVISITOR_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16
17#include <private/qduplicatetracker_p.h>
18#include <private/qqmljsannotation_p.h>
19#include <private/qqmljsast_p.h>
20#include <private/qqmljscontextualtypes_p.h>
21#include <private/qqmljsdiagnosticmessage_p.h>
22#include <private/qqmljsimporter_p.h>
23#include <private/qqmljslogger_p.h>
24#include <private/qqmljsscope_p.h>
25#include <private/qqmljsscopesbyid_p.h>
26#include <private/qv4compileddata_p.h>
27
28#include <QtQmlCompiler/qtqmlcompilerexports.h>
29
30#include <QtCore/qvariant.h>
31#include <QtCore/qstack.h>
32
33#include <functional>
34
35QT_BEGIN_NAMESPACE
36
37namespace QQmlJS::Dom {
38class QQmlDomAstCreatorWithQQmlJSScope;
39}
40
41struct QQmlJSResourceFileMapper;
42class Q_QMLCOMPILER_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor
43{
44public:
45 QQmlJSImportVisitor();
46 QQmlJSImportVisitor(const QQmlJSScope::Ptr &target,
47 QQmlJSImporter *importer, QQmlJSLogger *logger,
48 const QString &implicitImportDirectory,
49 const QStringList &qmldirFiles = QStringList());
50 ~QQmlJSImportVisitor();
51
52 using QQmlJS::AST::Visitor::endVisit;
53 using QQmlJS::AST::Visitor::postVisit;
54 using QQmlJS::AST::Visitor::preVisit;
55 using QQmlJS::AST::Visitor::visit;
56
57 QQmlJSScope::Ptr result() const { return m_exportedRootScope; }
58
59 const QQmlJSLogger *logger() const { return m_logger; }
60 QQmlJSLogger *logger() { return m_logger; }
61
62 QQmlJSImporter::ImportedTypes imports() const { return m_rootScopeImports; }
63 QQmlJSScopesById addressableScopes() const { return m_scopesById; }
64 QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> signalHandlers() const
65 {
66 return m_signalHandlers;
67 }
68 QList<QQmlJSScope::ConstPtr> qmlTypes() const { return m_qmlTypes; }
69 QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> scopesBylocation() const
70 {
71 return m_scopesByIrLocation;
72 }
73
74 static QString implicitImportDirectory(
75 const QString &localFile, QQmlJSResourceFileMapper *mapper);
76
77 // ### should this be restricted?
78 QQmlJSImporter *importer() { return m_importer; }
79 const QQmlJSImporter *importer() const { return m_importer; }
80
81 struct UnfinishedBinding
82 {
83 QQmlJSScope::Ptr owner;
84 std::function<QQmlJSMetaPropertyBinding()> create;
85 QQmlJSScope::BindingTargetSpecifier specifier = QQmlJSScope::SimplePropertyTarget;
86 };
87
88 QStringList seenModuleQualifiers() const { return m_seenModuleQualifiers; }
89
90protected:
91 bool visit(QQmlJS::AST::ExpressionStatement *ast) override;
92 void endVisit(QQmlJS::AST::ExpressionStatement *ast) override;
93
94 bool visit(QQmlJS::AST::UiProgram *) override;
95 void endVisit(QQmlJS::AST::UiProgram *) override;
96 bool visit(QQmlJS::AST::UiObjectDefinition *) override;
97 void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
98 bool visit(QQmlJS::AST::UiInlineComponent *) override;
99 void endVisit(QQmlJS::AST::UiInlineComponent *) override;
100 bool visit(QQmlJS::AST::UiPublicMember *) override;
101 void endVisit(QQmlJS::AST::UiPublicMember *) override;
102 bool visit(QQmlJS::AST::UiRequired *required) override;
103 bool visit(QQmlJS::AST::UiScriptBinding *) override;
104 void endVisit(QQmlJS::AST::UiScriptBinding *) override;
105 bool visit(QQmlJS::AST::UiArrayBinding *) override;
106 void endVisit(QQmlJS::AST::UiArrayBinding *) override;
107 bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override;
108 bool visit(QQmlJS::AST::FunctionExpression *fexpr) override;
109 void endVisit(QQmlJS::AST::FunctionExpression *) override;
110 bool visit(QQmlJS::AST::UiSourceElement *) override;
111 bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override;
112 void endVisit(QQmlJS::AST::FunctionDeclaration *) override;
113 bool visit(QQmlJS::AST::ClassExpression *ast) override;
114 void endVisit(QQmlJS::AST::ClassExpression *) override;
115 bool visit(QQmlJS::AST::UiImport *import) override;
116 bool visit(QQmlJS::AST::UiPragma *pragma) override;
117 bool visit(QQmlJS::AST::ClassDeclaration *ast) override;
118 void endVisit(QQmlJS::AST::ClassDeclaration *ast) override;
119 bool visit(QQmlJS::AST::ForStatement *ast) override;
120 void endVisit(QQmlJS::AST::ForStatement *ast) override;
121 bool visit(QQmlJS::AST::ForEachStatement *ast) override;
122 void endVisit(QQmlJS::AST::ForEachStatement *ast) override;
123 bool visit(QQmlJS::AST::Block *ast) override;
124 void endVisit(QQmlJS::AST::Block *ast) override;
125 bool visit(QQmlJS::AST::CaseBlock *ast) override;
126 void endVisit(QQmlJS::AST::CaseBlock *ast) override;
127 bool visit(QQmlJS::AST::Catch *ast) override;
128 void endVisit(QQmlJS::AST::Catch *ast) override;
129 bool visit(QQmlJS::AST::WithStatement *withStatement) override;
130 void endVisit(QQmlJS::AST::WithStatement *ast) override;
131
132 bool visit(QQmlJS::AST::VariableDeclarationList *vdl) override;
133 bool visit(QQmlJS::AST::FormalParameterList *fpl) override;
134
135 bool visit(QQmlJS::AST::UiObjectBinding *uiob) override;
136 void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override;
137
138 bool visit(QQmlJS::AST::ExportDeclaration *exp) override;
139 void endVisit(QQmlJS::AST::ExportDeclaration *exp) override;
140
141 bool visit(QQmlJS::AST::ESModule *module) override;
142 void endVisit(QQmlJS::AST::ESModule *module) override;
143
144 bool visit(QQmlJS::AST::Program *program) override;
145 void endVisit(QQmlJS::AST::Program *program) override;
146
147 void endVisit(QQmlJS::AST::FieldMemberExpression *) override;
148 bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
149
150 bool visit(QQmlJS::AST::PatternElement *) override;
151
152 bool visit(QQmlJS::AST::IfStatement *) override;
153
154 void throwRecursionDepthError() override;
155
156 virtual bool checkCustomParser(const QQmlJSScope::ConstPtr &scope);
157
158 void setScopeName(QQmlJSScope::Ptr &scope, QQmlJSScope::ScopeType type, const QString &name);
159
160 QString m_implicitImportDirectory;
161 QStringList m_qmldirFiles;
162 QQmlJSScope::Ptr m_currentScope;
163 const QQmlJSScope::Ptr m_exportedRootScope;
164 QQmlJSImporter *m_importer = nullptr;
165 QQmlJSLogger *m_logger = nullptr;
166
167 using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
168 using InlineComponentNameType = QQmlJSScope::InlineComponentNameType;
169 using InlineComponentOrDocumentRootName = QQmlJSScope::RootDocumentNameType;
170 QQmlJSScope::InlineComponentOrDocumentRootName m_currentRootName =
171 QQmlJSScope::RootDocumentNameType();
172 bool m_nextIsInlineComponent = false;
173 bool m_rootIsSingleton = false;
174 QQmlJSScope::Ptr m_savedBindingOuterScope;
175 QQmlJSScope::ConstPtr m_globalScope;
176 QQmlJSScopesById m_scopesById;
177 QQmlJSImporter::ImportedTypes m_rootScopeImports;
178 QList<QQmlJSScope::ConstPtr> m_qmlTypes;
179
180 // We need to record the locations as IR locations because those contain less data.
181 // This way we can look up objects by IR location later.
182 QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> m_scopesByIrLocation;
183
184 // Maps all qmlNames to the source location of their import
185 QMultiHash<QString, QQmlJS::SourceLocation> m_importTypeLocationMap;
186 // Maps all static modules to the source location of their import
187 QMultiHash<QString, QQmlJS::SourceLocation> m_importStaticModuleLocationMap;
188 // Contains all import source locations (could be extracted from above but that is expensive)
189 QSet<QQmlJS::SourceLocation> m_importLocations;
190 // A set of all types that have been used during type resolution
191 QSet<QString> m_usedTypes;
192
193 QList<UnfinishedBinding> m_bindings;
194 QSet<std::pair<const QQmlJSScope *, QString>> misplacedJSIdentifiers;
195
196 // stores JS functions and Script bindings per scope (only the name). mimics
197 // the content of QmlIR::Object::functionsAndExpressions
198 QHash<QQmlJSScope::ConstPtr, QList<QString>> m_functionsAndExpressions;
199
200 template <bool scopeIsConst = true>
201 struct ScopeAndNameT
202 {
203 using Scope = std::conditional_t<scopeIsConst, QQmlJSScope::ConstPtr, QQmlJSScope::Ptr>;
204
205 ScopeAndNameT() = default;
206 ScopeAndNameT(const Scope &scope, const QString &name) : scope(scope), name(name) { }
207 ScopeAndNameT(const ScopeAndNameT &) = default;
208 ScopeAndNameT(ScopeAndNameT &&) = default;
209 ScopeAndNameT &operator=(const ScopeAndNameT &) = default;
210 ScopeAndNameT &operator=(ScopeAndNameT &&) = default;
211 ~ScopeAndNameT() = default;
212
213 // Create const from non-const
214 ScopeAndNameT(typename std::enable_if<scopeIsConst, ScopeAndNameT<false>>::type &nonConst)
215 : scope(nonConst.scope), name(nonConst.name)
216 {
217 }
218
219 friend bool operator==(const ScopeAndNameT &lhs, const ScopeAndNameT &rhs)
220 {
221 return lhs.scope == rhs.scope && lhs.name == rhs.name;
222 }
223 friend bool operator!=(const ScopeAndNameT &lhs, const ScopeAndNameT &rhs)
224 {
225 return !(lhs == rhs);
226 }
227 friend size_t qHash(const ScopeAndNameT &san, size_t seed = 0)
228 {
229 return qHashMulti(seed, san.scope, san.name);
230 }
231
232 Scope scope;
233 QString name;
234 };
235 using ConstScopeAndName = ScopeAndNameT<true>;
236 using ScopeAndName = ScopeAndNameT<false>;
237
238 using FunctionOrExpressionIdentifier = ConstScopeAndName;
239 using Property = ConstScopeAndName;
240 using Alias = ConstScopeAndName;
241
242 // tells whether last-processed UiScriptBinding is truly a script binding
243 bool m_thisScriptBindingIsJavaScript = false;
244 QStack<FunctionOrExpressionIdentifier> m_functionStack;
245 // stores the number of functions inside each function
246 QHash<FunctionOrExpressionIdentifier, int> m_innerFunctions;
247 QQmlJSMetaMethod::RelativeFunctionIndex
248 addFunctionOrExpression(const QQmlJSScope::ConstPtr &scope, const QString &name);
249 void forgetFunctionExpression(const QString &name);
250 int synthesizeCompilationUnitRuntimeFunctionIndices(const QQmlJSScope::Ptr &scope,
251 int count) const;
252 void populateRuntimeFunctionIndicesForDocument() const;
253
254 void enterEnvironment(QQmlJSScope::ScopeType type, const QString &name,
255 const QQmlJS::SourceLocation &location);
256 // Finds an existing scope before attempting to create a new one. Returns \c
257 // true if the scope already exists and \c false if the new scope is created
258 bool enterEnvironmentNonUnique(QQmlJSScope::ScopeType type, const QString &name,
259 const QQmlJS::SourceLocation &location);
260 virtual void leaveEnvironment();
261
262 // A set of types that have not been resolved but have been used during the
263 // AST traversal
264 QDuplicateTracker<QQmlJSScope::ConstPtr> m_unresolvedTypes;
265 template<typename ErrorHandler>
266 bool checkTypeResolved(const QQmlJSScope::ConstPtr &type, ErrorHandler handle)
267 {
268 if (type->isFullyResolved() || checkCustomParser(scope: type))
269 return true;
270
271 // Note: ignore duplicates, but only after we are certain that the type
272 // is still unresolved
273 if (!m_unresolvedTypes.hasSeen(s: type))
274 handle(type);
275
276 return false;
277 }
278
279 bool checkTypeResolved(const QQmlJSScope::ConstPtr &type)
280 {
281 return checkTypeResolved(type, handle: [&](const QQmlJSScope::ConstPtr &type) {
282 warnUnresolvedType(type);
283 });
284 }
285
286 void warnUnresolvedType(const QQmlJSScope::ConstPtr &type) const;
287 void warnMissingPropertyForBinding(
288 const QString &property, const QQmlJS::SourceLocation &location,
289 const std::optional<QQmlJSFixSuggestion> &fixSuggestion = {});
290
291 QVector<QQmlJSAnnotation> parseAnnotations(QQmlJS::AST::UiAnnotationList *list);
292 void setAllBindings();
293 void addDefaultProperties();
294 void processDefaultProperties();
295 void processPropertyBindings();
296 void checkRequiredProperties();
297 void processPropertyTypes();
298 void processMethodTypes();
299 void processPropertyBindingObjects();
300 void flushPendingSignalParameters();
301
302 QQmlJSScope::ConstPtr scopeById(const QString &id, const QQmlJSScope::ConstPtr &current);
303
304 void breakInheritanceCycles(const QQmlJSScope::Ptr &scope);
305 void checkDeprecation(const QQmlJSScope::ConstPtr &scope);
306 void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
307 bool rootScopeIsValid() const { return m_exportedRootScope->sourceLocation().isValid(); }
308
309 enum class BindingExpressionParseResult { Invalid, Script, Literal, Translation };
310 enum class BindingForPropertyDefintion { Yes, No };
311 virtual BindingExpressionParseResult parseBindingExpression(
312 const QString &name, const QQmlJS::AST::Statement *statement,
313 const QQmlJS::AST::UiPublicMember *associatedPropertyDefinition = nullptr);
314 bool isImportPrefix(QString prefix) const;
315
316 // Used to temporarily store annotations for functions and generators wrapped in UiSourceElements
317 QVector<QQmlJSAnnotation> m_pendingMethodAnnotations;
318
319 struct PendingPropertyType
320 {
321 QQmlJSScope::Ptr scope;
322 QString name;
323 QQmlJS::SourceLocation location;
324 };
325
326 struct PendingMethodTypeAnnotations
327 {
328 QQmlJSScope::Ptr scope;
329 QString methodName;
330 // This keeps type annotations' locations in order (parameters then return type).
331 // If an annotation is not present, it is represented by an invalid source location.
332 QVarLengthArray<QQmlJS::SourceLocation, 3> locations;
333 };
334
335 struct PendingPropertyObjectBinding
336 {
337 QQmlJSScope::Ptr scope;
338 QQmlJSScope::Ptr childScope;
339 QString name;
340 QQmlJS::SourceLocation location;
341 bool onToken;
342 };
343
344 struct RequiredProperty
345 {
346 QQmlJSScope::Ptr scope;
347 QString name;
348 QQmlJS::SourceLocation location;
349 };
350
351 /*!
352 Utility wrapper that adds visibility scope to the data.
353
354 This wrapper becomes useful for binding processing where we need to know
355 both the property (or signal handler) owner and the scope in which the
356 binding is executed (the "visibility" scope).
357
358 As visibility scope (and data) does not typically have sufficient
359 information about a proper source location of that data, the location
360 also has to be provided to simplify the error reporting.
361 */
362 template<typename T>
363 struct WithVisibilityScope
364 {
365 QQmlJSScope::Ptr visibilityScope;
366 QQmlJS::SourceLocation dataLocation;
367 T data;
368 };
369
370 QHash<QQmlJSScope::Ptr, QVector<QQmlJSScope::Ptr>> m_pendingDefaultProperties;
371 QVector<PendingPropertyType> m_pendingPropertyTypes;
372 QVector<PendingMethodTypeAnnotations> m_pendingMethodTypeAnnotations;
373 QVector<PendingPropertyObjectBinding> m_pendingPropertyObjectBindings;
374 QVector<RequiredProperty> m_requiredProperties;
375 QVector<QQmlJSScope::Ptr> m_objectBindingScopes;
376 QVector<QQmlJSScope::Ptr> m_objectDefinitionScopes;
377
378 QHash<QQmlJSScope::Ptr, QVector<WithVisibilityScope<QString>>> m_propertyBindings;
379 QVector<Alias> m_aliasDefinitions;
380 QHash<Property, QList<Alias>> m_propertyAliases;
381
382 QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> m_signalHandlers;
383 QQmlJS::SourceLocation m_pendingSignalHandler;
384 QStringList m_seenModuleQualifiers;
385 QHash<QStringView, QQmlJS::SourceLocation> m_seenInlineComponents;
386
387private:
388 void registerTargetIntoImporter(const QQmlJSScope::Ptr &target);
389 void checkSignal(
390 const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location,
391 const QString &handlerName, const QStringList &handlerParameters);
392 void importBaseModules();
393 void resolveAliases();
394 void populatePropertyAliases();
395 void resolveGroupProperties();
396 void handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding);
397 virtual void handleLiteralBinding(const QQmlJSMetaPropertyBinding &,
398 const QQmlJS::AST::UiPublicMember *);
399
400 void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr);
401 void processImportWarnings(
402 const QString &what, const QList<QQmlJS::DiagnosticMessage> &warnings,
403 const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation());
404 void addImportWithLocation(
405 const QString &name, const QQmlJS::SourceLocation &loc, bool hadWarnings);
406 void populateCurrentScope(QQmlJSScope::ScopeType type, const QString &name,
407 const QQmlJS::SourceLocation &location);
408 void enterRootScope(QQmlJSScope::ScopeType type, const QString &name,
409 const QQmlJS::SourceLocation &location);
410
411 bool safeInsertJSIdentifier(QQmlJSScope::Ptr &scope, const QString &name,
412 const QQmlJSScope::JavaScriptIdentifier &identifier);
413
414 QList<QQmlJS::DiagnosticMessage> importFromHost(
415 const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location);
416 QList<QQmlJS::DiagnosticMessage> importFromQrc(
417 const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location);
418
419public:
420 friend class QQmlJS::Dom::QQmlDomAstCreatorWithQQmlJSScope;
421};
422
423QT_END_NAMESPACE
424
425#endif // QQMLJSIMPORTEDMEMBERSVISITOR_P_H
426

source code of qtdeclarative/src/qmlcompiler/qqmljsimportvisitor_p.h