Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmljsutils.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qqmljsutils_p.h"
7
8#include <algorithm>
9
11
12using namespace Qt::StringLiterals;
13
19template<typename ScopeForId>
21resolveAlias(ScopeForId scopeForId, const QQmlJSMetaProperty &property,
23{
24 Q_ASSERT(property.isAlias());
25 Q_ASSERT(owner);
26
28 result.owner = owner;
29
30 // TODO: one could optimize the generated alias code for aliases pointing to aliases
31 // e.g., if idA.myAlias -> idB.myAlias2 -> idC.myProp, then one could directly generate
32 // idA.myProp as pointing to idC.myProp.
33 // This gets complicated when idB.myAlias is in a different Component than where the
34 // idA.myAlias is defined: scopeForId currently only contains the ids of the current
35 // component and alias resolution on the ids of a different component fails then.
36 if (QQmlJSMetaProperty nextProperty = property; nextProperty.isAlias()) {
37 QQmlJSScope::ConstPtr resultOwner = result.owner;
39
40 visitor.reset();
41
42 auto aliasExprBits = nextProperty.aliasExpression().split(u'.');
43 // do not crash on invalid aliasexprbits when accessing aliasExprBits[0]
44 if (aliasExprBits.size() < 1)
45 return {};
46
47 // resolve id first:
48 resultOwner = scopeForId(aliasExprBits[0], resultOwner);
49 if (!resultOwner)
50 return {};
51
52 visitor.processResolvedId(resultOwner);
53
54 aliasExprBits.removeFirst(); // Note: for simplicity, remove the <id>
55 result.owner = resultOwner;
57
58 for (const QString &bit : std::as_const(aliasExprBits)) {
59 nextProperty = resultOwner->property(bit);
60 if (!nextProperty.isValid())
61 return {};
62
63 visitor.processResolvedProperty(nextProperty, resultOwner);
64
65 result.property = nextProperty;
66 result.owner = resultOwner;
68
69 resultOwner = nextProperty.type();
70 }
71 }
72
73 return result;
74}
75
78 const QQmlJSScope::ConstPtr &owner,
79 const AliasResolutionVisitor &visitor)
80{
81 return ::resolveAlias(
82 [&](const QString &id, const QQmlJSScope::ConstPtr &referrer) {
83 return typeResolver->scopeForId(id, referrer);
84 },
85 property, owner, visitor);
86}
87
90 const QQmlJSScope::ConstPtr &owner,
91 const AliasResolutionVisitor &visitor)
92{
93 return ::resolveAlias(
94 [&](const QString &id, const QQmlJSScope::ConstPtr &referrer) {
95 return idScopes.scope(id, referrer);
96 },
97 property, owner, visitor);
98}
99
100std::optional<QQmlJSFixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput,
101 QStringList candidates,
103{
104 QString shortestDistanceWord;
105 int shortestDistance = userInput.size();
106
107 // Most of the time the candidates are keys() from QHash, which means that
108 // running this function in the seemingly same setup might yield different
109 // best cadidate (e.g. imagine a typo 'thing' with candidates 'thingA' vs
110 // 'thingB'). This is especially flaky in e.g. test environment where the
111 // results may differ (even when the global hash seed is fixed!) when
112 // running one test vs the whole test suite (recall platform-dependent
113 // QSKIPs). There could be user-visible side effects as well, so just sort
114 // the candidates to guarantee consistent results
115 std::sort(candidates.begin(), candidates.end());
116
117 for (const QString &candidate : candidates) {
118 /*
119 * Calculate the distance between the userInput and candidate using Damerau–Levenshtein
120 * Roughly based on
121 * https://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows.
122 */
123 QList<int> v0(candidate.size() + 1);
124 QList<int> v1(candidate.size() + 1);
125
126 std::iota(v0.begin(), v0.end(), 0);
127
128 for (qsizetype i = 0; i < userInput.size(); i++) {
129 v1[0] = i + 1;
130 for (qsizetype j = 0; j < candidate.size(); j++) {
131 int deletionCost = v0[j + 1] + 1;
132 int insertionCost = v1[j] + 1;
133 int substitutionCost = userInput[i] == candidate[j] ? v0[j] : v0[j] + 1;
134 v1[j + 1] = std::min({ deletionCost, insertionCost, substitutionCost });
135 }
136 std::swap(v0, v1);
137 }
138
139 int distance = v0[candidate.size()];
140 if (distance < shortestDistance) {
141 shortestDistanceWord = candidate;
142 shortestDistance = distance;
143 }
144 }
145
146 if (shortestDistance
147 < std::min(std::max(userInput.size() / 2, qsizetype(3)), userInput.size())) {
148 return QQmlJSFixSuggestion {
149 u"Did you mean \"%1\"?"_s.arg(shortestDistanceWord),
150 location,
151 shortestDistanceWord
152 };
153 } else {
154 return {};
155 }
156}
157
163std::variant<QString, QQmlJS::DiagnosticMessage>
164QQmlJSUtils::sourceDirectoryPath(const QQmlJSImporter *importer, const QString &buildDirectoryPath)
165{
166 const auto makeError = [](const QString &msg) {
168 };
169
170 if (!importer->metaDataMapper())
171 return makeError(u"QQmlJSImporter::metaDataMapper() is nullptr"_s);
172
173 // for now, meta data contains just a single entry
178 if (!entry.isValid())
179 return makeError(u"Failed to find meta data entry in QQmlJSImporter::metaDataMapper()"_s);
180 if (!buildDirectoryPath.startsWith(entry.filePath)) // assume source directory path already
181 return makeError(u"The module output directory does not match the build directory path"_s);
182
183 QString qrcPath = buildDirectoryPath;
184 qrcPath.remove(0, entry.filePath.size());
185 qrcPath.prepend(entry.resourcePath);
186 qrcPath.remove(0, 1); // remove extra "/"
187
188 const QStringList sourceDirPaths = importer->resourceFileMapper()->filePaths(
190 if (sourceDirPaths.size() != 1) {
191 const QString matchedPaths =
192 sourceDirPaths.isEmpty() ? u"<none>"_s : sourceDirPaths.join(u", ");
193 return makeError(
194 QStringLiteral("QRC path %1 (deduced from %2) has unexpected number of mappings "
195 "(%3). File paths that matched:\n%4")
196 .arg(qrcPath, buildDirectoryPath, QString::number(sourceDirPaths.size()),
197 matchedPaths));
198 }
199 return sourceDirPaths[0];
200}
201
208 const QQmlJSRegisterContent &lhsContent,
209 const QQmlJSRegisterContent &rhsContent)
210{
211 Q_ASSERT(typeResolver);
212 const auto varType = typeResolver->varType();
213 const auto nullType = typeResolver->nullType();
214 const auto voidType = typeResolver->voidType();
215
216 // Use containedType() because nullptr is not a stored type.
217 const auto lhsType = typeResolver->containedType(lhsContent);
218 const auto rhsType = typeResolver->containedType(rhsContent);
219
220 return (typeResolver->equals(lhsType, varType)
221 && (typeResolver->equals(rhsType, nullType) || typeResolver->equals(rhsType, voidType)))
222 || (typeResolver->equals(rhsType, varType)
223 && (typeResolver->equals(lhsType, nullType)
224 || typeResolver->equals(lhsType, voidType)));
225}
226
233 const QQmlJSRegisterContent &lhsContent,
234 const QQmlJSRegisterContent &rhsContent)
235{
236 Q_ASSERT(typeResolver);
237 const auto lhsType = typeResolver->containedType(lhsContent);
238 const auto rhsType = typeResolver->containedType(rhsContent);
239 return (lhsType->isReferenceType()
240 && (rhsType->isReferenceType()
241 || typeResolver->equals(rhsType, typeResolver->nullType())))
242 || (rhsType->isReferenceType()
243 && (lhsType->isReferenceType()
244 || typeResolver->equals(lhsType, typeResolver->nullType())));
245}
246
252bool canCompareWithQUrl(const QQmlJSTypeResolver *typeResolver,
253 const QQmlJSRegisterContent &lhsContent,
254 const QQmlJSRegisterContent &rhsContent)
255{
256 Q_ASSERT(typeResolver);
257 const auto lhsType = typeResolver->containedType(lhsContent);
258 const auto rhsType = typeResolver->containedType(rhsContent);
259 return typeResolver->equals(lhsType, typeResolver->urlType())
260 && typeResolver->equals(rhsType, typeResolver->urlType());
261}
262
Definition qlist.h:74
QQmlJSResourceFileMapper * resourceFileMapper() const
QQmlJSResourceFileMapper * metaDataMapper() const
QQmlJSMetaProperty property(const QString &name) const
QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer) const
bool equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
QQmlJSScope::ConstPtr nullType() const
QQmlJSScope::ConstPtr containedType(const QQmlJSRegisterContent &container) const
QQmlJSScope::ConstPtr scopeForId(const QString &id, const QQmlJSScope::ConstPtr &referrer) const
QQmlJSScope::ConstPtr voidType() const
QQmlJSScope::ConstPtr urlType() const
QQmlJSScope::ConstPtr varType() const
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
QString & prepend(QChar c)
Definition qstring.h:411
Combined button and popup list for selecting options.
@ QtWarningMsg
Definition qlogging.h:31
GLint location
GLsizei GLsizei GLfloat distance
GLint GLfloat v0
GLint GLfloat GLfloat v1
GLuint entry
GLuint64EXT * result
[6]
bool canCompareWithQObject(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
bool canCompareWithQUrl(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
bool canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
static QQmlJSUtils::ResolvedAlias resolveAlias(ScopeForId scopeForId, const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &owner, const QQmlJSUtils::AliasResolutionVisitor &visitor)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(*) void arg)
#define QStringLiteral(str)
#define v1
#define v0
ptrdiff_t qsizetype
Definition qtypes.h:70
const char property[13]
Definition qwizard.cpp:101
Entry entry(const Filter &filter) const
static Filter resourceFileFilter(const QString &file)
QStringList filePaths(const Filter &filter) const
std::function< void(const QQmlJSMetaProperty &, const QQmlJSScope::ConstPtr &)> processResolvedProperty
std::function< void(const QQmlJSScope::ConstPtr &)> processResolvedId
QQmlJSScope::ConstPtr owner
static std::variant< QString, QQmlJS::DiagnosticMessage > sourceDirectoryPath(const QQmlJSImporter *importer, const QString &buildDirectoryPath)
static std::optional< QQmlJSFixSuggestion > didYouMean(const QString &userInput, QStringList candidates, QQmlJS::SourceLocation location)
static ResolvedAlias resolveAlias(const QQmlJSTypeResolver *typeResolver, const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &owner, const AliasResolutionVisitor &visitor)