Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmljsutils_p.h
Go to the documentation of this file.
1// Copyright (C) 2021 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 QQMLJSUTILS_P_H
5#define QQMLJSUTILS_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 <qtqmlcompilerexports.h>
18
19#include "qqmljslogger_p.h"
21#include "qqmljsscope_p.h"
22#include "qqmljsmetatypes_p.h"
23
24#include <QtCore/qdir.h>
25#include <QtCore/qstack.h>
26#include <QtCore/qstring.h>
27#include <QtCore/qstringbuilder.h>
28#include <QtCore/qstringview.h>
29
30#include <QtQml/private/qqmlsignalnames_p.h>
31#include <private/qduplicatetracker_p.h>
32
33#include <optional>
34#include <functional>
35#include <type_traits>
36#include <variant>
37
39
40namespace detail {
46template<typename To, typename From, typename std::enable_if_t<!std::is_pointer_v<To>, int> = 0>
47static auto getQQmlJSScopeFromSmartPtr(const From &p) -> From
48{
49 static_assert(!std::is_pointer_v<From>, "From has to be a smart pointer holding QQmlJSScope");
50 return p;
51}
52
60template<typename To, typename From, typename std::enable_if_t<std::is_pointer_v<To>, int> = 0>
61static auto getQQmlJSScopeFromSmartPtr(const From &p) -> decltype(p.get())
62{
63 static_assert(!std::is_pointer_v<From>, "From has to be a smart pointer holding QQmlJSScope");
64 return p.get();
65}
66}
67
70struct Q_QMLCOMPILER_EXPORT QQmlJSUtils
71{
77 {
78 using namespace Qt::StringLiterals;
79 return s.replace('\\'_L1, "\\\\"_L1)
80 .replace('"'_L1, "\\\""_L1)
81 .replace('\n'_L1, "\\n"_L1)
82 .replace('?'_L1, "\\?"_L1);
83 }
84
92 static QString toLiteral(const QString &s, QStringView ctor = u"QStringLiteral")
93 {
94 return ctor % u"(\"" % escapeString(s) % u"\")";
95 }
96
102 {
103 if (!type.endsWith(u'*'))
104 type = u"const " % type % u"&";
105 return type;
106 }
107
108 static std::optional<QQmlJSMetaProperty>
110 {
111 if (!signalName.endsWith(QLatin1String("Changed")))
112 return {};
113 constexpr int length = int(sizeof("Changed") / sizeof(char)) - 1;
114 signalName.chop(length);
115 auto p = scope->property(signalName.toString());
116 const bool isBindable = !p.bindable().isEmpty();
117 const bool canNotify = !p.notify().isEmpty();
118 if (p.isValid() && (isBindable || canNotify))
119 return p;
120 return {};
121 }
122
123 static std::optional<QQmlJSMetaProperty>
125 {
126 auto signalName = QQmlSignalNames::changedHandlerNameToPropertyName(changedHandler);
127 if (!signalName)
128 return {};
129
130 auto p = scope->property(*signalName);
131 const bool isBindable = !p.bindable().isEmpty();
132 const bool canNotify = !p.notify().isEmpty();
133 if (p.isValid() && (isBindable || canNotify))
134 return p;
135 return {};
136 }
137
138 static bool hasCompositeBase(const QQmlJSScope::ConstPtr &scope)
139 {
140 if (!scope)
141 return false;
142 const auto base = scope->baseType();
143 if (!base)
144 return false;
145 return base->isComposite() && base->scopeType() == QQmlSA::ScopeType::QMLScope;
146 }
147
160 PropertyAccessor accessor)
161 {
162 if (p.bindable().isEmpty())
163 return false;
164 switch (accessor) {
165 case PropertyAccessor::PropertyAccessor_Read:
166 return p.read() == QLatin1String("default");
167 case PropertyAccessor::PropertyAccessor_Write:
168 return p.write() == QLatin1String("default");
169 default:
170 break;
171 }
172 return false;
173 }
174
181 {
184 ResolvedAliasTarget kind = ResolvedAliasTarget::AliasTarget_Invalid;
185 };
187 {
188 std::function<void()> reset = []() {};
189 std::function<void(const QQmlJSScope::ConstPtr &)> processResolvedId =
190 [](const QQmlJSScope::ConstPtr &) {};
191 std::function<void(const QQmlJSMetaProperty &, const QQmlJSScope::ConstPtr &)>
192 processResolvedProperty =
193 [](const QQmlJSMetaProperty &, const QQmlJSScope::ConstPtr &) {};
194 };
195 static ResolvedAlias resolveAlias(const QQmlJSTypeResolver *typeResolver,
197 const QQmlJSScope::ConstPtr &owner,
198 const AliasResolutionVisitor &visitor);
199 static ResolvedAlias resolveAlias(const QQmlJSScopesById &idScopes,
201 const QQmlJSScope::ConstPtr &owner,
202 const AliasResolutionVisitor &visitor);
203
204 template<typename QQmlJSScopePtr, typename Action>
205 static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check)
206 {
207 if (!type)
208 return false;
209
210 using namespace detail;
211
212 // NB: among other things, getQQmlJSScopeFromSmartPtr() also resolves const
213 // vs non-const pointer issue, so use it's return value as the type
214 using T = decltype(getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(
215 std::declval<QQmlJSScope::ConstPtr>()));
216
217 const auto checkWrapper = [&](const auto &scope, QQmlJSScope::ExtensionKind mode) {
218 if constexpr (std::is_invocable<Action, decltype(scope),
220 return check(scope, mode);
221 } else {
222 static_assert(std::is_invocable<Action, decltype(scope)>::value,
223 "Inferred type Action has unexpected arguments");
224 Q_UNUSED(mode);
225 return check(scope);
226 }
227 };
228
229 const bool isValueOrSequenceType = [type]() {
230 switch (type->accessSemantics()) {
231 case QQmlJSScope::AccessSemantics::Value:
232 case QQmlJSScope::AccessSemantics::Sequence:
233 return true;
234 default:
235 break;
236 }
237 return false;
238 }();
239
240 QDuplicateTracker<T> seen;
241 for (T scope = type; scope && !seen.hasSeen(scope);
242 scope = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(scope->baseType())) {
243 QDuplicateTracker<T> seenExtensions;
244 // Extensions override the types they extend. However, usually base
245 // types of extensions are ignored. The unusual cases are when we
246 // have a value or sequence type or when we have the QObject type, in which
247 // case we also study the extension's base type hierarchy.
248 const bool isQObject = scope->internalName() == QLatin1String("QObject");
249 auto [extensionPtr, extensionKind] = scope->extensionType();
250 auto extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(extensionPtr);
251 do {
252 if (!extension || seenExtensions.hasSeen(extension))
253 break;
254
255 if (checkWrapper(extension, extensionKind))
256 return true;
257 extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(extension->baseType());
258 } while (isValueOrSequenceType || isQObject);
259
260 if (checkWrapper(scope, QQmlJSScope::NotExtension))
261 return true;
262 }
263
264 return false;
265 }
266
267 template<typename Action>
268 static void traverseFollowingQmlIrObjectStructure(const QQmlJSScope::Ptr &root, Action act)
269 {
270 // We *have* to perform DFS here: QmlIR::Object entries within the
271 // QmlIR::Document are stored in the order they appear during AST traversal
272 // (which does DFS)
273 QStack<QQmlJSScope::Ptr> stack;
274 stack.push(root);
275
276 while (!stack.isEmpty()) {
277 QQmlJSScope::Ptr current = stack.pop();
278
279 act(current);
280
281 auto children = current->childScopes();
282 // arrays are special: they are reverse-processed in QmlIRBuilder
283 if (!current->isArrayScope())
284 std::reverse(children.begin(), children.end()); // left-to-right DFS
285 stack.append(std::move(children));
286 }
287 }
288
299 template<typename Action>
301 const QQmlJSScope::ConstPtr &start, Action act)
302 {
303 // Meta objects are arranged in the following way:
304 // * static meta objects are chained first
305 // * dynamic meta objects are added on top - they come from extensions.
306 // QQmlVMEMetaObject ignored here
307 //
308 // Example:
309 // ```
310 // class A : public QObject {
311 // QML_EXTENDED(Ext)
312 // };
313 // class B : public A {
314 // QML_EXTENDED(Ext2)
315 // };
316 // ```
317 // gives: Ext2 -> Ext -> B -> A -> QObject
318 // ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
319 // ^^^^^^^^^^^ static meta objects
320 // dynamic meta objects
321
322 using namespace Qt::StringLiterals;
323 // ignore special extensions
324 const QLatin1String ignoredExtensionNames[] = {
325 // QObject extensions: (not related to C++)
326 "Object"_L1,
327 "ObjectPrototype"_L1,
328 };
329
330 QList<QQmlJSScope::AnnotatedScope> types;
331 QList<QQmlJSScope::AnnotatedScope> extensions;
332 const auto collect = [&](const QQmlJSScope::ConstPtr &type, QQmlJSScope::ExtensionKind m) {
335 return false;
336 }
337
338 for (const auto &name : ignoredExtensionNames) {
339 if (type->internalName() == name)
340 return false;
341 }
342 extensions.append(QQmlJSScope::AnnotatedScope { type, m });
343 return false;
344 };
345 searchBaseAndExtensionTypes(scope, collect);
346
347 QList<QQmlJSScope::AnnotatedScope> all;
348 all.reserve(extensions.size() + types.size());
349 // first extensions then types
350 all.append(std::move(extensions));
351 all.append(std::move(types));
352
353 auto begin = all.cbegin();
354 // skip to start
355 while (begin != all.cend() && !begin->scope->isSameType(start))
356 ++begin;
357
358 // iterate over extensions and types starting at a specified point
359 for (; begin != all.cend(); ++begin)
360 act(begin->scope, begin->extensionSpecifier);
361 }
362
363 static std::optional<QQmlJSFixSuggestion> didYouMean(const QString &userInput,
364 QStringList candidates,
366
367 static std::variant<QString, QQmlJS::DiagnosticMessage>
368 sourceDirectoryPath(const QQmlJSImporter *importer, const QString &buildDirectoryPath);
369
370 template <typename Container>
371 static void deduplicate(Container &container)
372 {
373 std::sort(container.begin(), container.end());
374 auto erase = std::unique(container.begin(), container.end());
375 container.erase(erase, container.end());
376 }
377
379 {
380 for (QString &path : paths)
382 return paths;
383 }
384};
385
386bool Q_QMLCOMPILER_EXPORT canStrictlyCompareWithVar(
387 const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
388 const QQmlJSScope::ConstPtr &rhsType);
389
390bool Q_QMLCOMPILER_EXPORT canCompareWithQObject(
391 const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
392 const QQmlJSScope::ConstPtr &rhsType);
393
394bool Q_QMLCOMPILER_EXPORT canCompareWithQUrl(
395 const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
396 const QQmlJSScope::ConstPtr &rhsType);
397
399
400#endif // QQMLJSUTILS_P_H
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
QString bindable() const
QVector< QQmlJSScope::Ptr > childScopes()
bool isArrayScope() const
QQmlJSMetaProperty property(const QString &name) const
QQmlJSScope::ConstPtr baseType() const
static std::optional< QString > changedHandlerNameToPropertyName(QStringView handler)
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void extension()
[6]
Definition dialogs.cpp:230
Combined button and popup list for selecting options.
static auto getQQmlJSScopeFromSmartPtr(const From &p) -> From
qsizetype erase(QByteArray &ba, const T &t)
Definition qbytearray.h:782
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
GLint location
GLenum mode
const GLfloat * m
GLsizei GLenum GLenum * types
GLenum GLuint GLenum GLsizei length
GLenum type
GLsizei const GLuint * paths
GLuint start
GLuint name
GLdouble s
[6]
Definition qopenglext.h:235
GLboolean reset
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
static QQmlJSUtils::ResolvedAlias resolveAlias(ScopeForId scopeForId, const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &owner, const QQmlJSUtils::AliasResolutionVisitor &visitor)
bool Q_QMLCOMPILER_EXPORT canCompareWithQObject(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
bool Q_QMLCOMPILER_EXPORT canCompareWithQUrl(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
bool Q_QMLCOMPILER_EXPORT canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_UNUSED(x)
static const uint base
Definition qurlidna.cpp:20
const char property[13]
Definition qwizard.cpp:101
QQmlJSScope::ConstPtr owner
QQmlJSMetaProperty property
static QString escapeString(QString s)
static void traverseFollowingQmlIrObjectStructure(const QQmlJSScope::Ptr &root, Action act)
static QString toLiteral(const QString &s, QStringView ctor=u"QStringLiteral")
static std::optional< QQmlJSMetaProperty > propertyFromChangedHandler(const QQmlJSScope::ConstPtr &scope, QStringView changedHandler)
static QStringList cleanPaths(QStringList &&paths)
static std::optional< QQmlJSMetaProperty > changeHandlerProperty(const QQmlJSScope::ConstPtr &scope, QStringView signalName)
static void deduplicate(Container &container)
static bool hasCompositeBase(const QQmlJSScope::ConstPtr &scope)
static void traverseFollowingMetaObjectHierarchy(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &start, Action act)
static QString constRefify(QString type)
static bool bindablePropertyHasDefaultAccessor(const QQmlJSMetaProperty &p, PropertyAccessor accessor)
static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check)