27 const QQmlJSScope::ConstPtr &owner,
const QQmlJSUtils::AliasResolutionVisitor &visitor)
32 QQmlJSUtils::ResolvedAlias result {};
41 QQmlJSScope::ConstPtr resultOwner = result.owner;
42 result = QQmlJSUtils::ResolvedAlias {};
46 auto aliasExprBits = nextProperty.aliasExpression().split(u'.');
48 if (aliasExprBits.size() < 1)
52 resultOwner = scopeForId(aliasExprBits[0], resultOwner);
56 visitor.processResolvedId(resultOwner);
58 aliasExprBits.removeFirst();
59 result.owner = resultOwner;
60 result.kind = QQmlJSUtils::AliasTarget_Object;
62 for (
const QString &bit : std::as_const(aliasExprBits)) {
63 nextProperty = resultOwner->property(bit);
64 if (!nextProperty.isValid())
67 visitor.processResolvedProperty(nextProperty, resultOwner);
69 result.property = nextProperty;
70 result.owner = resultOwner;
71 result.kind = QQmlJSUtils::AliasTarget_Property;
73 resultOwner = nextProperty.type();
80QQmlJSUtils::ResolvedAlias QQmlJSUtils::resolveAlias(
const QQmlJSTypeResolver *typeResolver,
81 const QQmlJSMetaProperty &property,
82 const QQmlJSScope::ConstPtr &owner,
83 const AliasResolutionVisitor &visitor)
85 return ::resolveAlias(
86 [&](
const QString &id,
const QQmlJSScope::ConstPtr &referrer) {
87 return typeResolver->typeForId(referrer, id);
89 property, owner, visitor);
92QQmlJSUtils::ResolvedAlias QQmlJSUtils::resolveAlias(
const QQmlJSScopesById &idScopes,
93 const QQmlJSMetaProperty &property,
94 const QQmlJSScope::ConstPtr &owner,
95 const AliasResolutionVisitor &visitor)
97 return ::resolveAlias(
98 [&](
const QString &id,
const QQmlJSScope::ConstPtr &referrer) {
99 return idScopes.scope(id, referrer);
101 property, owner, visitor);
104std::optional<QQmlJSFixSuggestion> QQmlJSUtils::didYouMean(
const QString &userInput,
105 QStringList candidates,
106 QQmlJS::SourceLocation location)
108 QString shortestDistanceWord;
109 int shortestDistance = userInput.size();
119 std::sort(candidates.begin(), candidates.end());
121 for (
const QString &candidate : candidates) {
123
124
125
126
127 QVarLengthArray<
int> v0(candidate.size() + 1);
128 QVarLengthArray<
int> v1(candidate.size() + 1);
130 std::iota(v0.begin(), v0.end(), 0);
132 for (qsizetype i = 0; i < userInput.size(); i++) {
134 for (qsizetype j = 0; j < candidate.size(); j++) {
135 int deletionCost = v0[j + 1] + 1;
136 int insertionCost = v1[j] + 1;
137 int substitutionCost = userInput[i] == candidate[j] ? v0[j] : v0[j] + 1;
138 v1[j + 1] = std::min({ deletionCost, insertionCost, substitutionCost });
143 int distance = v0[candidate.size()];
144 if (distance < shortestDistance) {
145 shortestDistanceWord = candidate;
146 shortestDistance = distance;
151 < std::min(std::max(userInput.size() / 2, qsizetype(3)), userInput.size())) {
152 return QQmlJSFixSuggestion {
153 u"Did you mean \"%1\"?"_s.arg(shortestDistanceWord),
168QQmlJSUtils::sourceDirectoryPath(
const QQmlJSImporter *importer,
const QString &buildDirectoryPath)
170 const auto makeError = [](
const QString &msg) {
171 return QQmlJS::DiagnosticMessage { msg, QtWarningMsg, QQmlJS::SourceLocation() };
174 if (!importer->metaDataMapper())
175 return makeError(u"QQmlJSImporter::metaDataMapper() is nullptr"_s);
178 QQmlJSResourceFileMapper::Filter matchAll { QString(), QStringList(),
179 QQmlJSResourceFileMapper::Directory
180 | QQmlJSResourceFileMapper::Recurse };
181 QQmlJSResourceFileMapper::Entry entry = importer->metaDataMapper()->entry(matchAll);
182 if (!entry.isValid())
183 return makeError(u"Failed to find meta data entry in QQmlJSImporter::metaDataMapper()"_s);
184 if (!buildDirectoryPath.startsWith(entry.filePath))
185 return makeError(u"The module output directory does not match the build directory path"_s);
187 QString qrcPath = buildDirectoryPath;
188 qrcPath.remove(0, entry.filePath.size());
189 qrcPath.prepend(entry.resourcePath);
190 qrcPath.remove(0, 1);
192 const QStringList sourceDirPaths = importer->resourceFileMapper()->filePaths(
193 QQmlJSResourceFileMapper::resourceFileFilter(qrcPath));
194 if (sourceDirPaths.size() != 1) {
195 const QString matchedPaths =
196 sourceDirPaths.isEmpty() ? u"<none>"_s : sourceDirPaths.join(u", ");
198 QStringLiteral(
"QRC path %1 (deduced from %2) has unexpected number of mappings "
199 "(%3). File paths that matched:\n%4")
200 .arg(qrcPath, buildDirectoryPath, QString::number(sourceDirPaths.size()),
203 return sourceDirPaths[0];
212 const QQmlJSTypeResolver *typeResolver,
const QQmlJSScope::ConstPtr &lhsType,
213 const QQmlJSScope::ConstPtr &rhsType)
215 Q_ASSERT(typeResolver);
217 const QQmlJSScope::ConstPtr varType = typeResolver->varType();
218 const bool leftIsVar = (lhsType == varType);
219 const bool righttIsVar = (rhsType == varType);
220 return leftIsVar != righttIsVar;
229 const QQmlJSTypeResolver *typeResolver,
const QQmlJSScope::ConstPtr &lhsType,
230 const QQmlJSScope::ConstPtr &rhsType)
232 Q_ASSERT(typeResolver);
233 return (lhsType->isReferenceType()
234 && (rhsType->isReferenceType() || rhsType == typeResolver->nullType()))
235 || (rhsType->isReferenceType()
236 && (lhsType->isReferenceType() || lhsType == typeResolver->nullType()));
245 const QQmlJSTypeResolver *typeResolver,
const QQmlJSScope::ConstPtr &lhsType,
246 const QQmlJSScope::ConstPtr &rhsType)
248 Q_ASSERT(typeResolver);
249 return lhsType == typeResolver->urlType() && rhsType == typeResolver->urlType();
252QStringList QQmlJSUtils::resourceFilesFromBuildFolders(
const QStringList &buildFolders)
255 for (
const QString &path : buildFolders) {
256 for (
auto it : QDirListing{ path, QStringList{ u"*.qrc"_s },
257 QDirListing::IteratorFlag::Recursive
258 | QDirListing::IteratorFlag::FilesOnly
259 | QDirListing::IteratorFlag::IncludeHidden }) {
260 result.append(it.filePath());
280 const QString &pathInBuildFolder,
FilterType type)
282 const QString cleanPath = QDir::cleanPath(pathInBuildFolder);
283 QStringView directoryPath = cleanPath;
285 while (!directoryPath.isEmpty()) {
286 const qsizetype lastSlashIndex = directoryPath.lastIndexOf(u'/');
287 if (lastSlashIndex == -1)
290 directoryPath.truncate(lastSlashIndex);
291 const QString qmldirPath = u"%1/qmldir"_s.arg(directoryPath);
292 const QQmlJSResourceFileMapper::Filter qmldirFilter = type ==
LocalFileFilter
293 ? QQmlJSResourceFileMapper::localFileFilter(qmldirPath)
294 : QQmlJSResourceFileMapper::resourceFileFilter(qmldirPath);
296 QQmlJSResourceFileMapper::Entry result = mapper->entry(qmldirFilter);
297 if (result.isValid()) {
298 result.resourcePath.chop(std::char_traits<
char>::length(
"/qmldir"));
299 result.filePath.chop(std::char_traits<
char>::length(
"/qmldir"));
313QString QQmlJSUtils::qmlSourcePathFromBuildPath(
const QQmlJSResourceFileMapper *mapper,
314 const QString &pathInBuildFolder)
317 return pathInBuildFolder;
319 const auto qmlModuleEntry =
320 qmlModuleEntryFromBuildPath(mapper, pathInBuildFolder, LocalFileFilter);
321 if (!qmlModuleEntry.isValid())
322 return pathInBuildFolder;
323 const QString qrcPath = qmlModuleEntry.resourcePath
324 + QStringView(pathInBuildFolder).sliced(qmlModuleEntry.filePath.size());
326 const auto entry = mapper->entry(QQmlJSResourceFileMapper::resourceFileFilter(qrcPath));
327 return entry.isValid()? entry.filePath : pathInBuildFolder;
335QString QQmlJSUtils::qmlBuildPathFromSourcePath(
const QQmlJSResourceFileMapper *mapper,
336 const QString &pathInSourceFolder)
339 return pathInSourceFolder;
341 const QString qrcPath =
342 mapper->entry(QQmlJSResourceFileMapper::localFileFilter(pathInSourceFolder))
345 if (qrcPath.isEmpty())
346 return pathInSourceFolder;
348 const auto moduleBuildEntry =
349 qmlModuleEntryFromBuildPath(mapper, qrcPath, ResourceFileFilter);
351 if (!moduleBuildEntry.isValid())
352 return pathInSourceFolder;
354 const auto qrcFolderPath = qrcPath.first(qrcPath.lastIndexOf(u'/'));
356 return moduleBuildEntry.filePath + qrcFolderPath.sliced(moduleBuildEntry.resourcePath.size())
357 + pathInSourceFolder.sliced(pathInSourceFolder.lastIndexOf(u'/'));
static QQmlJSUtils::ResolvedAlias resolveAlias(ScopeForId scopeForId, const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &owner, const QQmlJSUtils::AliasResolutionVisitor &visitor)