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 QSet<QQmlJSScope::ConstPtr> literalScopesToCheck() const { return m_literalScopesToCheck; }
69 QList<QQmlJSScope::ConstPtr> qmlTypes() const { return m_qmlTypes; }
70 QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> scopesBylocation() const
71 {
72 return m_scopesByIrLocation;
73 }
74
75 static QString implicitImportDirectory(
76 const QString &localFile, QQmlJSResourceFileMapper *mapper);
77
78 // ### should this be restricted?
79 QQmlJSImporter *importer() { return m_importer; }
80 const QQmlJSImporter *importer() const { return m_importer; }
81
82 struct UnfinishedBinding
83 {
84 QQmlJSScope::Ptr owner;
85 std::function<QQmlJSMetaPropertyBinding()> create;
86 QQmlJSScope::BindingTargetSpecifier specifier = QQmlJSScope::SimplePropertyTarget;
87 };
88
89 QStringList seenModuleQualifiers() const { return m_seenModuleQualifiers; }
90
91protected:
92 // Linter warnings, we might want to move this at some point
93 bool visit(QQmlJS::AST::StringLiteral *) override;
94
95 bool visit(QQmlJS::AST::ExpressionStatement *ast) override;
96 void endVisit(QQmlJS::AST::ExpressionStatement *ast) override;
97
98 bool visit(QQmlJS::AST::UiProgram *) override;
99 void endVisit(QQmlJS::AST::UiProgram *) override;
100 bool visit(QQmlJS::AST::UiObjectDefinition *) override;
101 void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
102 bool visit(QQmlJS::AST::UiInlineComponent *) override;
103 void endVisit(QQmlJS::AST::UiInlineComponent *) override;
104 bool visit(QQmlJS::AST::UiPublicMember *) override;
105 void endVisit(QQmlJS::AST::UiPublicMember *) override;
106 bool visit(QQmlJS::AST::UiRequired *required) override;
107 bool visit(QQmlJS::AST::UiScriptBinding *) override;
108 void endVisit(QQmlJS::AST::UiScriptBinding *) override;
109 bool visit(QQmlJS::AST::UiArrayBinding *) override;
110 void endVisit(QQmlJS::AST::UiArrayBinding *) override;
111 bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override;
112 bool visit(QQmlJS::AST::FunctionExpression *fexpr) override;
113 void endVisit(QQmlJS::AST::FunctionExpression *) override;
114 bool visit(QQmlJS::AST::UiSourceElement *) override;
115 bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override;
116 void endVisit(QQmlJS::AST::FunctionDeclaration *) override;
117 bool visit(QQmlJS::AST::ClassExpression *ast) override;
118 void endVisit(QQmlJS::AST::ClassExpression *) override;
119 bool visit(QQmlJS::AST::UiImport *import) override;
120 bool visit(QQmlJS::AST::UiPragma *pragma) override;
121 bool visit(QQmlJS::AST::ClassDeclaration *ast) override;
122 void endVisit(QQmlJS::AST::ClassDeclaration *ast) override;
123 bool visit(QQmlJS::AST::ForStatement *ast) override;
124 void endVisit(QQmlJS::AST::ForStatement *ast) override;
125 bool visit(QQmlJS::AST::ForEachStatement *ast) override;
126 void endVisit(QQmlJS::AST::ForEachStatement *ast) override;
127 bool visit(QQmlJS::AST::Block *ast) override;
128 void endVisit(QQmlJS::AST::Block *ast) override;
129 bool visit(QQmlJS::AST::CaseBlock *ast) override;
130 void endVisit(QQmlJS::AST::CaseBlock *ast) override;
131 bool visit(QQmlJS::AST::Catch *ast) override;
132 void endVisit(QQmlJS::AST::Catch *ast) override;
133 bool visit(QQmlJS::AST::WithStatement *withStatement) override;
134 void endVisit(QQmlJS::AST::WithStatement *ast) override;
135
136 bool visit(QQmlJS::AST::VariableDeclarationList *vdl) override;
137 bool visit(QQmlJS::AST::FormalParameterList *fpl) override;
138
139 bool visit(QQmlJS::AST::UiObjectBinding *uiob) override;
140 void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override;
141
142 bool visit(QQmlJS::AST::ExportDeclaration *exp) override;
143 void endVisit(QQmlJS::AST::ExportDeclaration *exp) override;
144
145 bool visit(QQmlJS::AST::ESModule *module) override;
146 void endVisit(QQmlJS::AST::ESModule *module) override;
147
148 bool visit(QQmlJS::AST::Program *program) override;
149 void endVisit(QQmlJS::AST::Program *program) override;
150
151 void endVisit(QQmlJS::AST::FieldMemberExpression *) override;
152 bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
153
154 bool visit(QQmlJS::AST::PatternElement *) override;
155
156 void throwRecursionDepthError() override;
157
158 QString m_implicitImportDirectory;
159 QStringList m_qmldirFiles;
160 QQmlJSScope::Ptr m_currentScope;
161 const QQmlJSScope::Ptr m_exportedRootScope;
162 QQmlJSImporter *m_importer = nullptr;
163 QQmlJSLogger *m_logger = nullptr;
164
165 using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
166 using InlineComponentNameType = QQmlJSScope::InlineComponentNameType;
167 using InlineComponentOrDocumentRootName = QQmlJSScope::RootDocumentNameType;
168 QQmlJSScope::InlineComponentOrDocumentRootName m_currentRootName =
169 QQmlJSScope::RootDocumentNameType();
170 bool m_nextIsInlineComponent = false;
171 bool m_rootIsSingleton = false;
172 QQmlJSScope::Ptr m_savedBindingOuterScope;
173 QQmlJSScope::ConstPtr m_globalScope;
174 QQmlJSScopesById m_scopesById;
175 QQmlJSImporter::ImportedTypes m_rootScopeImports;
176 QList<QQmlJSScope::ConstPtr> m_qmlTypes;
177
178 // We need to record the locations as IR locations because those contain less data.
179 // This way we can look up objects by IR location later.
180 QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> m_scopesByIrLocation;
181
182 // Maps all qmlNames to the source location of their import
183 QMultiHash<QString, QQmlJS::SourceLocation> m_importTypeLocationMap;
184 // Maps all static modules to the source location of their import
185 QMultiHash<QString, QQmlJS::SourceLocation> m_importStaticModuleLocationMap;
186 // Contains all import source locations (could be extracted from above but that is expensive)
187 QSet<QQmlJS::SourceLocation> m_importLocations;
188 // A set of all types that have been used during type resolution
189 QSet<QString> m_usedTypes;
190
191 QList<UnfinishedBinding> m_bindings;
192
193 // stores JS functions and Script bindings per scope (only the name). mimics
194 // the content of QmlIR::Object::functionsAndExpressions
195 QHash<QQmlJSScope::ConstPtr, QList<QString>> m_functionsAndExpressions;
196
197 struct FunctionOrExpressionIdentifier
198 {
199 QQmlJSScope::ConstPtr scope;
200 QString name;
201 friend bool operator==(const FunctionOrExpressionIdentifier &x,
202 const FunctionOrExpressionIdentifier &y)
203 {
204 return x.scope == y.scope && x.name == y.name;
205 }
206 friend bool operator!=(const FunctionOrExpressionIdentifier &x,
207 const FunctionOrExpressionIdentifier &y)
208 {
209 return !(x == y);
210 }
211 friend size_t qHash(const FunctionOrExpressionIdentifier &x, size_t seed = 0)
212 {
213 return qHashMulti(seed, args: x.scope, args: x.name);
214 }
215 };
216
217 // tells whether last-processed UiScriptBinding is truly a script binding
218 bool m_thisScriptBindingIsJavaScript = false;
219 QStack<FunctionOrExpressionIdentifier> m_functionStack;
220 // stores the number of functions inside each function
221 QHash<FunctionOrExpressionIdentifier, int> m_innerFunctions;
222 QQmlJSMetaMethod::RelativeFunctionIndex
223 addFunctionOrExpression(const QQmlJSScope::ConstPtr &scope, const QString &name);
224 void forgetFunctionExpression(const QString &name);
225 int synthesizeCompilationUnitRuntimeFunctionIndices(const QQmlJSScope::Ptr &scope,
226 int count) const;
227 void populateRuntimeFunctionIndicesForDocument() const;
228
229 void enterEnvironment(QQmlJSScope::ScopeType type, const QString &name,
230 const QQmlJS::SourceLocation &location);
231 // Finds an existing scope before attempting to create a new one. Returns \c
232 // true if the scope already exists and \c false if the new scope is created
233 bool enterEnvironmentNonUnique(QQmlJSScope::ScopeType type, const QString &name,
234 const QQmlJS::SourceLocation &location);
235 void leaveEnvironment();
236
237 // A set of types that have not been resolved but have been used during the
238 // AST traversal
239 QDuplicateTracker<QQmlJSScope::ConstPtr> m_unresolvedTypes;
240 template<typename ErrorHandler>
241 bool checkTypeResolved(const QQmlJSScope::ConstPtr &type, ErrorHandler handle)
242 {
243 if (type->isFullyResolved() || type->isInCustomParserParent())
244 return true;
245
246 // Note: ignore duplicates, but only after we are certain that the type
247 // is still unresolved
248 if (!m_unresolvedTypes.hasSeen(s: type))
249 handle(type);
250
251 return false;
252 }
253
254 bool checkTypeResolved(const QQmlJSScope::ConstPtr &type)
255 {
256 return checkTypeResolved(type, handle: [&](const QQmlJSScope::ConstPtr &type) {
257 warnUnresolvedType(type);
258 });
259 }
260
261 void warnUnresolvedType(const QQmlJSScope::ConstPtr &type) const;
262 void warnMissingPropertyForBinding(
263 const QString &property, const QQmlJS::SourceLocation &location,
264 const std::optional<QQmlJSFixSuggestion> &fixSuggestion = {});
265
266 QVector<QQmlJSAnnotation> parseAnnotations(QQmlJS::AST::UiAnnotationList *list);
267 void setAllBindings();
268 void addDefaultProperties();
269 void processDefaultProperties();
270 void processPropertyBindings();
271 void checkRequiredProperties();
272 void processPropertyTypes();
273 void processMethodTypes();
274 void processPropertyBindingObjects();
275 void flushPendingSignalParameters();
276
277 QQmlJSScope::ConstPtr scopeById(const QString &id, const QQmlJSScope::ConstPtr &current);
278
279 void breakInheritanceCycles(const QQmlJSScope::Ptr &scope);
280 void checkDeprecation(const QQmlJSScope::ConstPtr &scope);
281 void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
282 bool rootScopeIsValid() const { return m_exportedRootScope->sourceLocation().isValid(); }
283
284 enum class BindingExpressionParseResult { Invalid, Script, Literal, Translation };
285 BindingExpressionParseResult parseBindingExpression(const QString &name,
286 const QQmlJS::AST::Statement *statement);
287 bool isImportPrefix(QString prefix) const;
288
289 // Used to temporarily store annotations for functions and generators wrapped in UiSourceElements
290 QVector<QQmlJSAnnotation> m_pendingMethodAnnotations;
291
292 struct PendingPropertyType
293 {
294 QQmlJSScope::Ptr scope;
295 QString name;
296 QQmlJS::SourceLocation location;
297 };
298
299 struct PendingMethodTypeAnnotations
300 {
301 QQmlJSScope::Ptr scope;
302 QString methodName;
303 // This keeps type annotations' locations in order (parameters then return type).
304 // If an annotation is not present, it is represented by an invalid source location.
305 QVarLengthArray<QQmlJS::SourceLocation, 3> locations;
306 };
307
308 struct PendingPropertyObjectBinding
309 {
310 QQmlJSScope::Ptr scope;
311 QQmlJSScope::Ptr childScope;
312 QString name;
313 QQmlJS::SourceLocation location;
314 bool onToken;
315 };
316
317 struct RequiredProperty
318 {
319 QQmlJSScope::Ptr scope;
320 QString name;
321 QQmlJS::SourceLocation location;
322 };
323
324 /*!
325 Utility wrapper that adds visibility scope to the data.
326
327 This wrapper becomes useful for binding processing where we need to know
328 both the property (or signal handler) owner and the scope in which the
329 binding is executed (the "visibility" scope).
330
331 As visibility scope (and data) does not typically have sufficient
332 information about a proper source location of that data, the location
333 also has to be provided to simplify the error reporting.
334 */
335 template<typename T>
336 struct WithVisibilityScope
337 {
338 QQmlJSScope::Ptr visibilityScope;
339 QQmlJS::SourceLocation dataLocation;
340 T data;
341 };
342
343 QHash<QQmlJSScope::Ptr, QVector<QQmlJSScope::Ptr>> m_pendingDefaultProperties;
344 QVector<PendingPropertyType> m_pendingPropertyTypes;
345 QVector<PendingMethodTypeAnnotations> m_pendingMethodTypeAnnotations;
346 QVector<PendingPropertyObjectBinding> m_pendingPropertyObjectBindings;
347 QVector<RequiredProperty> m_requiredProperties;
348 QVector<QQmlJSScope::Ptr> m_objectBindingScopes;
349 QVector<QQmlJSScope::Ptr> m_objectDefinitionScopes;
350
351 QHash<QQmlJSScope::Ptr, QVector<WithVisibilityScope<QString>>> m_propertyBindings;
352
353 QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> m_signalHandlers;
354 QSet<QQmlJSScope::ConstPtr> m_literalScopesToCheck;
355 QQmlJS::SourceLocation m_pendingSignalHandler;
356 QStringList m_seenModuleQualifiers;
357
358private:
359 void checkSignal(
360 const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location,
361 const QString &handlerName, const QStringList &handlerParameters);
362 void importBaseModules();
363 void resolveAliases();
364 void resolveGroupProperties();
365 void handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding);
366
367 void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr);
368 void processImportWarnings(
369 const QString &what, const QList<QQmlJS::DiagnosticMessage> &warnings,
370 const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation());
371 void addImportWithLocation(
372 const QString &name, const QQmlJS::SourceLocation &loc, bool hadWarnings);
373 void populateCurrentScope(QQmlJSScope::ScopeType type, const QString &name,
374 const QQmlJS::SourceLocation &location);
375 void enterRootScope(QQmlJSScope::ScopeType type, const QString &name,
376 const QQmlJS::SourceLocation &location);
377
378 QList<QQmlJS::DiagnosticMessage> importFromHost(
379 const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location);
380 QList<QQmlJS::DiagnosticMessage> importFromQrc(
381 const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location);
382
383public:
384 friend class QQmlJS::Dom::QQmlDomAstCreatorWithQQmlJSScope;
385};
386
387QT_END_NAMESPACE
388
389#endif // QQMLJSIMPORTEDMEMBERSVISITOR_P_H
390

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