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 | |
35 | QT_BEGIN_NAMESPACE |
36 | |
37 | namespace QQmlJS::Dom { |
38 | class QQmlDomAstCreatorWithQQmlJSScope; |
39 | } |
40 | |
41 | struct QQmlJSResourceFileMapper; |
42 | class Q_QMLCOMPILER_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor |
43 | { |
44 | public: |
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 | |
91 | protected: |
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 ¤t); |
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 | |
358 | private: |
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 | |
383 | public: |
384 | friend class QQmlJS::Dom::QQmlDomAstCreatorWithQQmlJSScope; |
385 | }; |
386 | |
387 | QT_END_NAMESPACE |
388 | |
389 | #endif // QQMLJSIMPORTEDMEMBERSVISITOR_P_H |
390 | |