qmllint: Warn about failing imports

Warn when an import fails instead of silently ignoring them.
Without these it was impossible to tell whether a type was not resolved due to an unresolvable import or due to other reasons.

It also fixes the previous behavior of just dumping the types of qmltypes into the rootScope if manually imported.
Now they are only are present when actually importing one of the modules mentioned in the type's export statement.

Change-Id: I62bdd99828702ed02512eaac30f7f99b98d1df28
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Maximilian Goldstein 2021-03-19 13:11:03 +01:00
parent b07d653c09
commit 6ddb78735b
5 changed files with 46 additions and 19 deletions

View File

@ -269,25 +269,40 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
/*!
* Imports types from the specified \a qmltypesFiles.
*/
QQmlJSImporter::ImportedTypes QQmlJSImporter::importQmltypes(const QStringList &qmltypesFiles)
void QQmlJSImporter::importQmltypes(const QStringList &qmltypesFiles)
{
AvailableTypes types(builtinImportHelper().cppNames);
Import result;
for (const auto &qmltypeFile : qmltypesFiles)
for (const auto &qmltypeFile : qmltypesFiles) {
Import result;
readQmltypes(qmltypeFile, &result.objects, &result.dependencies);
importDependencies(result, &types);
processImport(result, &types);
// Append _FAKE_QMLDIR to our made up qmldir name so that if it ever gets used somewhere else except for cache lookups,
// it will blow up due to a missing file instead of producing weird results.
const QString qmldirName = qmltypeFile + QStringLiteral("_FAKE_QMLDIR");
m_seenQmldirFiles.insert(qmldirName, result);
return types.qmlNames;
for (const auto &object : result.objects.values()) {
for (const auto &ex : object->exports()) {
m_seenImports.insert({ex.package(), ex.version()}, qmldirName);
// We also have to handle the case that no version is provided
m_seenImports.insert({ex.package(), QTypeRevision()}, qmldirName);
}
}
}
}
QQmlJSImporter::ImportedTypes QQmlJSImporter::importModule(
const QString &module, const QString &prefix, QTypeRevision version)
const QString &module, const QString &prefix, QTypeRevision version, QQmlJS::SourceLocation location)
{
AvailableTypes result(builtinImportHelper().cppNames);
importHelper(module, &result, prefix, version);
if (!importHelper(module, &result, prefix, version)) {
m_warnings.append({
QStringLiteral("Failed to import %1. Are your include paths set up properly?").arg(module),
QtWarningMsg,
location
});
}
return result.qmlNames;
}
@ -296,20 +311,22 @@ QQmlJSImporter::ImportedTypes QQmlJSImporter::builtinInternalNames()
return builtinImportHelper().cppNames;
}
void QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
const QString &prefix, QTypeRevision version)
{
const QPair<QString, QTypeRevision> importId { module, version };
const auto it = m_seenImports.constFind(importId);
if (it != m_seenImports.constEnd()) {
if (it->isEmpty())
return; // TODO: warn here in the future
return false;
const auto import = m_seenQmldirFiles.constFind(*it);
Q_ASSERT(import != m_seenQmldirFiles.constEnd());
importDependencies(*import, types, prefix, version);
processImport(*import, types, prefix);
return;
return true;
}
const auto modulePaths = qQmlResolveImportPaths(module, m_importPaths, version);
@ -321,7 +338,7 @@ void QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
m_seenImports.insert(importId, qmldirPath);
importDependencies(*it, types, prefix, version);
processImport(*it, types, prefix);
return;
return true;
}
const QFileInfo file(qmldirPath);
@ -331,12 +348,13 @@ void QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
m_seenImports.insert(importId, qmldirPath);
importDependencies(import, types, prefix, version);
processImport(import, types, prefix);
return;
return true;
}
}
// TODO: warn here in the future
m_seenImports.insert(importId, QString());
return false;
}
QQmlJSScope::Ptr QQmlJSImporter::localFile2ScopeTree(const QString &filePath)

View File

@ -59,14 +59,15 @@ public:
QQmlJSResourceFileMapper *resourceFileMapper() { return m_mapper; }
ImportedTypes importBuiltins();
ImportedTypes importQmltypes(const QStringList &qmltypesFiles);
void importQmltypes(const QStringList &qmltypesFiles);
QQmlJSScope::Ptr importFile(const QString &file);
ImportedTypes importDirectory(const QString &directory, const QString &prefix = QString());
ImportedTypes importModule(
const QString &module, const QString &prefix = QString(),
QTypeRevision version = QTypeRevision());
QTypeRevision version = QTypeRevision(),
QQmlJS::SourceLocation location = QQmlJS::SourceLocation());
ImportedTypes builtinInternalNames();
@ -101,7 +102,7 @@ private:
};
AvailableTypes builtinImportHelper();
void importHelper(const QString &module, AvailableTypes *types,
bool importHelper(const QString &module, AvailableTypes *types,
const QString &prefix = QString(),
QTypeRevision version = QTypeRevision());
void processImport(const Import &import, AvailableTypes *types,

View File

@ -176,7 +176,7 @@ void QQmlJSImportVisitor::importBaseModules()
m_rootScopeImports = m_importer->importBuiltins();
if (!m_qmltypesFiles.isEmpty())
m_rootScopeImports.insert(m_importer->importQmltypes(m_qmltypesFiles));
m_importer->importQmltypes(m_qmltypesFiles);
m_rootScopeImports.insert(m_importer->importDirectory(m_implicitImportDirectory));
m_errors.append(m_importer->takeWarnings());
@ -538,7 +538,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
path.chop(1);
const auto imported = m_importer->importModule(
path, prefix, import->version ? import->version->version : QTypeRevision());
path, prefix, import->version ? import->version->version : QTypeRevision(), import->firstSourceLocation());
m_rootScopeImports.insert(imported);

View File

@ -0,0 +1,4 @@
import QtQml
import FooBar
QtObject {}

View File

@ -379,6 +379,10 @@ void TestQmllint::dirtyQmlCode_data()
<< QStringLiteral("defaultPropertyWithWrongType2.qml")
<< QStringLiteral("Cannot assign to default property of incompatible type")
<< QStringLiteral("Cannot assign to non-existent default property");
QTest::newRow("InvalidImport")
<< QStringLiteral("invalidImport.qml")
<< QStringLiteral("Failed to import FooBar. Are your include paths set up properly?")
<< QString();
}
void TestQmllint::dirtyQmlCode()