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());
288 const QString &pathInBuildFolder,
FilterType type)
290 const QString cleanPath = QDir::cleanPath(pathInBuildFolder);
291 QStringView directoryPath = cleanPath;
293 while (!directoryPath.isEmpty()) {
294 const qsizetype lastSlashIndex = directoryPath.lastIndexOf(u'/');
295 if (lastSlashIndex == -1)
298 directoryPath.truncate(lastSlashIndex);
299 const QString qmldirPath = u"%1/qmldir"_s.arg(directoryPath);
300 const QQmlJSResourceFileMapper::Filter qmldirFilter = type ==
LocalFileFilter
301 ? QQmlJSResourceFileMapper::localFileFilter(qmldirPath)
302 : QQmlJSResourceFileMapper::resourceFileFilter(qmldirPath);
304 QQmlJSResourceFileMapper::Entry result = mapper->entry(qmldirFilter);
305 if (result.isValid()) {
306 result.resourcePath.chop(std::char_traits<
char>::length(
"/qmldir"));
307 result.filePath.chop(std::char_traits<
char>::length(
"/qmldir"));
334QString qmlSourcePathFromBuildPath(
const QQmlJSResourceFileMapper *mapper,
335 const QString &pathInBuildFolder)
338 return pathInBuildFolder;
341 const QString buildPath = QDir::cleanPath(QFileInfo(pathInBuildFolder).absoluteFilePath());
342 const auto qmlModuleEntry = qmlModuleEntryFromBuildPath(mapper, buildPath, LocalFileFilter);
343 if (!qmlModuleEntry.isValid())
344 return pathInBuildFolder;
345 const QString qrcPath = qmlModuleEntry.resourcePath
346 + QStringView(buildPath).sliced(qmlModuleEntry.filePath.size());
348 const auto entry = mapper->entry(QQmlJSResourceFileMapper::resourceFileFilter(qrcPath));
349 return entry.isValid()? entry.filePath : pathInBuildFolder;
367QString qmlBuildPathFromSourcePath(
const QQmlJSResourceFileMapper *mapper,
368 const QString &pathInSourceFolder)
371 return pathInSourceFolder;
375 const auto allEntries =
376 mapper->filter(QQmlJSResourceFileMapper::localFileFilter(pathInSourceFolder));
378 for (
const auto &mapEntry : allEntries) {
379 const QString qrcPath = mapEntry.resourcePath;
380 if (qrcPath.isEmpty())
383 const auto moduleBuildEntry =
384 qmlModuleEntryFromBuildPath(mapper, qrcPath, ResourceFileFilter);
385 if (!moduleBuildEntry.isValid())
388 return moduleBuildEntry.filePath + qrcPath.sliced(moduleBuildEntry.resourcePath.size());
391 return pathInSourceFolder;
398QString getScopeName(
const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ScopeType type)
403 return scope->internalName();
405 if (!scope->isComposite())
406 return scope->internalName();
408 if (scope->isInlineComponent() && scope->inlineComponentName().has_value())
409 return scope->inlineComponentName().value();
411 if (scope->isFileRootComponent())
412 return QFileInfo(scope->filePath()).baseName();
414 return scope->baseTypeName();