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