28 const QQmlJSScope::ConstPtr &owner,
43 QQmlJSScope::ConstPtr resultOwner = result.owner;
44 result = ResolvedAlias{ };
48 auto aliasExprBits = nextProperty.aliasExpression().split(u'.');
50 if (aliasExprBits.size() < 1)
54 resultOwner = scopeForId(aliasExprBits[0], resultOwner);
58 visitor.processResolvedId(resultOwner);
60 aliasExprBits.removeFirst();
61 result.owner = resultOwner;
64 for (
const QString &bit : std::as_const(aliasExprBits)) {
65 nextProperty = resultOwner->property(bit);
66 if (!nextProperty.isValid())
69 visitor.processResolvedProperty(nextProperty, resultOwner);
71 result.property = nextProperty;
72 result.owner = resultOwner;
73 result.kind = AliasTarget_Property;
75 resultOwner = nextProperty.type();
87 [&](
const QString &id,
const QQmlJSScope::ConstPtr &referrer) {
88 return typeResolver->typeForId(referrer, id);
90 property, owner, visitor);
104std::optional<QQmlJSFixSuggestion> didYouMean(
const QString &userInput, QStringList candidates,
105 const QString &filename,
106 const 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),
155 { QQmlJSDocumentEdit{ filename, location, shortestDistanceWord } }
168sourceDirectoryPath(
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];
211bool canStrictlyCompareWithVar(
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;
228bool canCompareWithQObject(
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()));
244bool canCompareWithQUrl(
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();
258QStringList resourceFilesFromBuildFolders(
const QStringList &buildFolders)
261 for (
const QString &path : buildFolders) {
262 for (
auto it : QDirListing{ path, QStringList{ u"*.qrc"_s },
263 QDirListing::IteratorFlag::Recursive
264 | QDirListing::IteratorFlag::FilesOnly
265 | QDirListing::IteratorFlag::IncludeHidden }) {
266 result.append(it.filePath());
286 const QString &pathInBuildFolder,
FilterType type)
288 const QString cleanPath = QDir::cleanPath(pathInBuildFolder);
289 QStringView directoryPath = cleanPath;
291 while (!directoryPath.isEmpty()) {
292 const qsizetype lastSlashIndex = directoryPath.lastIndexOf(u'/');
293 if (lastSlashIndex == -1)
296 directoryPath.truncate(lastSlashIndex);
297 const QString qmldirPath = u"%1/qmldir"_s.arg(directoryPath);
298 const QQmlJSResourceFileMapper::Filter qmldirFilter = type ==
LocalFileFilter
299 ? QQmlJSResourceFileMapper::localFileFilter(qmldirPath)
300 : QQmlJSResourceFileMapper::resourceFileFilter(qmldirPath);
302 QQmlJSResourceFileMapper::Entry result = mapper->entry(qmldirFilter);
303 if (result.isValid()) {
304 result.resourcePath.chop(std::char_traits<
char>::length(
"/qmldir"));
305 result.filePath.chop(std::char_traits<
char>::length(
"/qmldir"));
319QString qmlSourcePathFromBuildPath(
const QQmlJSResourceFileMapper *mapper,
320 const QString &pathInBuildFolder)
323 return pathInBuildFolder;
325 const auto qmlModuleEntry =
326 qmlModuleEntryFromBuildPath(mapper, pathInBuildFolder, LocalFileFilter);
327 if (!qmlModuleEntry.isValid())
328 return pathInBuildFolder;
329 const QString qrcPath = qmlModuleEntry.resourcePath
330 + QStringView(pathInBuildFolder).sliced(qmlModuleEntry.filePath.size());
332 const auto entry = mapper->entry(QQmlJSResourceFileMapper::resourceFileFilter(qrcPath));
333 return entry.isValid()? entry.filePath : pathInBuildFolder;
341QString qmlBuildPathFromSourcePath(
const QQmlJSResourceFileMapper *mapper,
342 const QString &pathInSourceFolder)
345 return pathInSourceFolder;
349 const auto allEntries =
350 mapper->filter(QQmlJSResourceFileMapper::localFileFilter(pathInSourceFolder));
352 for (
const auto &mapEntry : allEntries) {
353 const QString qrcPath = mapEntry.resourcePath;
354 if (qrcPath.isEmpty())
357 const auto moduleBuildEntry =
358 qmlModuleEntryFromBuildPath(mapper, qrcPath, ResourceFileFilter);
359 if (!moduleBuildEntry.isValid())
362 const auto qrcFolderPath = qrcPath.first(qrcPath.lastIndexOf(u'/'));
363 return moduleBuildEntry.filePath + qrcFolderPath.sliced(moduleBuildEntry.resourcePath.size())
364 + pathInSourceFolder.sliced(pathInSourceFolder.lastIndexOf(u'/'));
367 return pathInSourceFolder;
374QString getScopeName(
const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ScopeType type)
379 return scope->internalName();
381 if (!scope->isComposite())
382 return scope->internalName();
384 if (scope->isInlineComponent() && scope->inlineComponentName().has_value())
385 return scope->inlineComponentName().value();
387 if (scope->isFileRootComponent())
388 return QFileInfo(scope->filePath()).baseName();
390 return scope->baseTypeName();