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::FormalParameterList *fpl) override;
139
140 bool visit(QQmlJS::AST::UiObjectBinding *uiob) override;
141 void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override;
142
143 bool visit(QQmlJS::AST::ExportDeclaration *exp) override;
144 void endVisit(QQmlJS::AST::ExportDeclaration *exp) override;
145
146 bool visit(QQmlJS::AST::ESModule *module) override;
147 void endVisit(QQmlJS::AST::ESModule *module) override;
148
149 bool visit(QQmlJS::AST::Program *program) override;
150 void endVisit(QQmlJS::AST::Program *program) override;
151
152 void endVisit(QQmlJS::AST::FieldMemberExpression *) override;
153 bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
154
155 bool visit(QQmlJS::AST::PatternElement *) override;
156
157 bool visit(QQmlJS::AST::IfStatement *) override;
158
159 void throwRecursionDepthError() override;
160
161 virtual bool checkCustomParser(const QQmlJSScope::ConstPtr &scope);
162
163 void setScopeName(QQmlJSScope::Ptr &scope, QQmlJSScope::ScopeType type, const QString &name);
164 virtual bool safeInsertJSIdentifier(QQmlJSScope::Ptr &scope, const QString &name,
165 const QQmlJSScope::JavaScriptIdentifier &identifier);
166
167 QString m_implicitImportDirectory;
168 QStringList m_qmldirFiles;
169 QQmlJSScope::Ptr m_currentScope;
170 const QQmlJSScope::Ptr m_exportedRootScope;
171 QQmlJSImporter *m_importer = nullptr;
172 QQmlJSLogger *m_logger = nullptr;
173
174 using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
175 using InlineComponentNameType = QQmlJSScope::InlineComponentNameType;
176 using InlineComponentOrDocumentRootName = QQmlJSScope::RootDocumentNameType;
177 QQmlJSScope::InlineComponentOrDocumentRootName m_currentRootName =
178 QQmlJSScope::RootDocumentNameType();
179 bool m_nextIsInlineComponent = false;
180 bool m_rootIsSingleton = false;
181 QQmlJSScope::Ptr m_savedBindingOuterScope;
182 QQmlJSScope::ConstPtr m_globalScope;
183 QQmlJSScopesById m_scopesById;
184 QQmlJSImporter::ImportedTypes m_rootScopeImports;
185 QList<QQmlJSScope::ConstPtr> m_qmlTypes;
186
187 // We need to record the locations as IR locations because those contain less data.
188 // This way we can look up objects by IR location later.
189 QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> m_scopesByIrLocation;
190
191 // Maps all qmlNames to the source location of their import
192 QMultiHash<QString, QQmlJS::SourceLocation> m_importTypeLocationMap;
193 // Maps all static modules to the source location of their import
194 QMultiHash<QString, QQmlJS::SourceLocation> m_importStaticModuleLocationMap;
195 // Contains all import source locations (could be extracted from above but that is expensive)
196 QSet<QQmlJS::SourceLocation> m_importLocations;
197 // A set of all types that have been used during type resolution
198 QSet<QString> m_usedTypes;
199
200 QList<UnfinishedBinding> m_bindings;
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 QList<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 void checkForComponentTypeWithProperties(const QQmlJSScope::ConstPtr &scope);
312 bool rootScopeIsValid() const { return m_exportedRootScope->sourceLocation().isValid(); }
313
314 enum class BindingExpressionParseResult { Invalid, Script, Literal, Translation };
315 enum class BindingForPropertyDefintion { Yes, No };
316 virtual BindingExpressionParseResult parseBindingExpression(
317 const QString &name, const QQmlJS::AST::Statement *statement,
318 const QQmlJS::AST::UiPublicMember *associatedPropertyDefinition = nullptr);
319 bool isImportPrefix(QString prefix) const;
320
321 // Used to temporarily store annotations for functions and generators wrapped in UiSourceElements
322 QList<QQmlJSAnnotation> m_pendingMethodAnnotations;
323
324 struct PendingPropertyType
325 {
326 QQmlJSScope::Ptr scope;
327 QString name;
328 QQmlJS::SourceLocation location;
329 };
330
331 struct PendingMethodTypeAnnotations
332 {
333 QQmlJSScope::Ptr scope;
334 QString methodName;
335 // This keeps type annotations' locations in order (parameters then return type).
336 // If an annotation is not present, it is represented by an invalid source location.
337 QVarLengthArray<QQmlJS::SourceLocation, 3> locations;
338 };
339
340 struct PendingPropertyObjectBinding
341 {
342 QQmlJSScope::Ptr scope;
343 QQmlJSScope::Ptr childScope;
344 QString name;
345 QQmlJS::SourceLocation location;
346 bool onToken;
347 };
348
349 struct RequiredProperty
350 {
351 QQmlJSScope::Ptr scope;
352 QString name;
353 QQmlJS::SourceLocation location;
354 };
355
356 /*!
357 Utility wrapper that adds visibility scope to the data.
358
359 This wrapper becomes useful for binding processing where we need to know
360 both the property (or signal handler) owner and the scope in which the
361 binding is executed (the "visibility" scope).
362
363 As visibility scope (and data) does not typically have sufficient
364 information about a proper source location of that data, the location
365 also has to be provided to simplify the error reporting.
366 */
367 template<typename T>
368 struct WithVisibilityScope
369 {
370 QQmlJSScope::Ptr visibilityScope;
371 QQmlJS::SourceLocation dataLocation;
372 T data;
373 };
374
375 QHash<QQmlJSScope::Ptr, QList<QQmlJSScope::Ptr>> m_pendingDefaultProperties;
376 QList<PendingPropertyType> m_pendingPropertyTypes;
377 QList<PendingMethodTypeAnnotations> m_pendingMethodTypeAnnotations;
378 QList<PendingPropertyObjectBinding> m_pendingPropertyObjectBindings;
379 QList<RequiredProperty> m_requiredProperties;
380 QList<QQmlJSScope::Ptr> m_objectBindingScopes;
381 QList<QQmlJSScope::Ptr> m_objectDefinitionScopes;
382
383 QHash<QQmlJSScope::Ptr, QList<WithVisibilityScope<QString>>> m_propertyBindings;
384 QList<Alias> m_aliasDefinitions;
385 QHash<Property, QList<Alias>> m_propertyAliases;
386
387 QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> m_signalHandlers;
388 QQmlJS::SourceLocation m_pendingSignalHandler;
389 QStringList m_seenModuleQualifiers;
390 QHash<QStringView, QQmlJS::SourceLocation> m_seenInlineComponents;
391
392private:
393 void registerTargetIntoImporter(const QQmlJSScope::Ptr &target);
394 void checkSignal(
395 const QQmlJSScope::ConstPtr &signalScope, const QQmlJS::SourceLocation &location,
396 const QString &handlerName, const QStringList &handlerParameters);
397 void importBaseModules();
398 void resolveAliases();
399 void populatePropertyAliases();
400 void resolveGroupProperties();
401 void handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding);
402 virtual void handleLiteralBinding(const QQmlJSMetaPropertyBinding &,
403 const QQmlJS::AST::UiPublicMember *);
404
405 void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr);
406 void processImportWarnings(
407 const QString &what, const QList<QQmlJS::DiagnosticMessage> &warnings,
408 const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation());
409 void addImportWithLocation(
410 const QString &name, const QQmlJS::SourceLocation &loc, bool hadWarnings);
411 void populateCurrentScope(QQmlJSScope::ScopeType type, const QString &name,
412 const QQmlJS::SourceLocation &location);
413 void enterRootScope(QQmlJSScope::ScopeType type, const QString &name,
414 const QQmlJS::SourceLocation &location);
415
416 QList<QQmlJS::DiagnosticMessage> importFromHost(
417 const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location);
418 QList<QQmlJS::DiagnosticMessage> importFromQrc(
419 const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location);
420
421public:
422 friend class QQmlJS::Dom::QQmlDomAstCreatorWithQQmlJSScope;
423};
424
425QT_END_NAMESPACE
426
427#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)
Combined button and popup list for selecting options.
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)