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.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 const QQmlJSRegisterContent content = typeResolver->scopedType(referrer, id);
85 return content.type();
86 return QQmlJSScope::ConstPtr();
87 },
88 property, owner, visitor);
89}
90
93 const QQmlJSScope::ConstPtr &owner,
94 const AliasResolutionVisitor &visitor)
95{
96 return ::resolveAlias(
97 [&](const QString &id, const QQmlJSScope::ConstPtr &referrer) {
98 return idScopes.scope(id, referrer);
99 },
100 property, owner, visitor);
101}
102
103std::optional<QQmlJSFixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput,
104 QStringList candidates,
106{
107 QString shortestDistanceWord;
108 int shortestDistance = userInput.size();
109
110 // Most of the time the candidates are keys() from QHash, which means that
111 // running this function in the seemingly same setup might yield different
112 // best cadidate (e.g. imagine a typo 'thing' with candidates 'thingA' vs
113 // 'thingB'). This is especially flaky in e.g. test environment where the
114 // results may differ (even when the global hash seed is fixed!) when
115 // running one test vs the whole test suite (recall platform-dependent
116 // QSKIPs). There could be user-visible side effects as well, so just sort
117 // the candidates to guarantee consistent results
118 std::sort(candidates.begin(), candidates.end());
119
120 for (const QString &candidate : candidates) {
121 /*
122 * Calculate the distance between the userInput and candidate using Damerau–Levenshtein
123 * Roughly based on
124 * https://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows.
125 */
126 QList<int> v0(candidate.size() + 1);
127 QList<int> v1(candidate.size() + 1);
128
129 std::iota(v0.begin(), v0.end(), 0);
130
131 for (qsizetype i = 0; i < userInput.size(); i++) {
132 v1[0] = i + 1;
133 for (qsizetype j = 0; j < candidate.size(); j++) {
134 int deletionCost = v0[j + 1] + 1;
135 int insertionCost = v1[j] + 1;
136 int substitutionCost = userInput[i] == candidate[j] ? v0[j] : v0[j] + 1;
137 v1[j + 1] = std::min({ deletionCost, insertionCost, substitutionCost });
138 }
139 std::swap(v0, v1);
140 }
141
142 int distance = v0[candidate.size()];
143 if (distance < shortestDistance) {
144 shortestDistanceWord = candidate;
145 shortestDistance = distance;
146 }
147 }
148
149 if (shortestDistance
150 < std::min(std::max(userInput.size() / 2, qsizetype(3)), userInput.size())) {
151 return QQmlJSFixSuggestion {
152 u"Did you mean \"%1\"?"_s.arg(shortestDistanceWord),
153 location,
154 shortestDistanceWord
155 };
156 } else {
157 return {};
158 }
159}
160
166std::variant<QString, QQmlJS::DiagnosticMessage>
167QQmlJSUtils::sourceDirectoryPath(const QQmlJSImporter *importer, const QString &buildDirectoryPath)
168{
169 const auto makeError = [](const QString &msg) {
171 };
172
173 if (!importer->metaDataMapper())
174 return makeError(u"QQmlJSImporter::metaDataMapper() is nullptr"_s);
175
176 // for now, meta data contains just a single entry
181 if (!entry.isValid())
182 return makeError(u"Failed to find meta data entry in QQmlJSImporter::metaDataMapper()"_s);
183 if (!buildDirectoryPath.startsWith(entry.filePath)) // assume source directory path already
184 return makeError(u"The module output directory does not match the build directory path"_s);
185
186 QString qrcPath = buildDirectoryPath;
187 qrcPath.remove(0, entry.filePath.size());
188 qrcPath.prepend(entry.resourcePath);
189 qrcPath.remove(0, 1); // remove extra "/"
190
191 const QStringList sourceDirPaths = importer->resourceFileMapper()->filePaths(
193 if (sourceDirPaths.size() != 1) {
194 const QString matchedPaths =
195 sourceDirPaths.isEmpty() ? u"<none>"_s : sourceDirPaths.join(u", ");
196 return makeError(
197 QStringLiteral("QRC path %1 (deduced from %2) has unexpected number of mappings "
198 "(%3). File paths that matched:\n%4")
199 .arg(qrcPath, buildDirectoryPath, QString::number(sourceDirPaths.size()),
200 matchedPaths));
201 }
202 return sourceDirPaths[0];
203}
204
211 const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
212 const QQmlJSScope::ConstPtr &rhsType)
213{
214 Q_ASSERT(typeResolver);
215
216 const QQmlJSScope::ConstPtr varType = typeResolver->varType();
217 const bool leftIsVar = typeResolver->equals(lhsType, varType);
218 const bool righttIsVar = typeResolver->equals(rhsType, varType);
219 return leftIsVar != righttIsVar;
220}
221
228 const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
229 const QQmlJSScope::ConstPtr &rhsType)
230{
231 Q_ASSERT(typeResolver);
232 return (lhsType->isReferenceType()
233 && (rhsType->isReferenceType()
234 || typeResolver->equals(rhsType, typeResolver->nullType())))
235 || (rhsType->isReferenceType()
236 && (lhsType->isReferenceType()
237 || typeResolver->equals(lhsType, typeResolver->nullType())));
238}
239
246 const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
247 const QQmlJSScope::ConstPtr &rhsType)
248{
249 Q_ASSERT(typeResolver);
250 return typeResolver->equals(lhsType, typeResolver->urlType())
251 && typeResolver->equals(rhsType, typeResolver->urlType());
252}
253
QQmlJSResourceFileMapper * resourceFileMapper() const
QQmlJSResourceFileMapper * metaDataMapper() const
ContentVariant variant() const
QQmlJSScope::ConstPtr type() const
QDeferredSharedPointer< const QQmlJSScope > ConstPtr
bool equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
QQmlJSScope::ConstPtr nullType() const
QQmlJSScope::ConstPtr urlType() const
QQmlJSRegisterContent scopedType(const QQmlJSScope::ConstPtr &scope, const QString &name, int lookupIndex=QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSScopesByIdOptions options=Default) const
QQmlJSScope::ConstPtr varType() const
\inmodule QtCore
\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
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
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:8084
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:3466
QString & prepend(QChar c)
Definition qstring.h:478
Combined button and popup list for selecting options.
QList< QString > QStringList
Constructs a string list that contains the given string, str.
@ QtWarningMsg
Definition qlogging.h:31
GLint location
GLsizei GLsizei GLfloat distance
GLint GLfloat v0
GLint GLfloat GLfloat v1
GLuint entry
GLuint64EXT * result
[6]
bool canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
bool canCompareWithQObject(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
bool canCompareWithQUrl(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
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:165
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)