qmllint: Resolve imported files by qrc path if available
If a file is part of the resource file system, the implicit directory is also in the resource file system. We can use QQmlJSResourceFileMapper to figure this out. Change-Id: I48d86c308c7ec38d9971b84e7f059daa3baf63ee Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
74bf3c8c33
commit
7ecd4b6d1f
|
@ -346,6 +346,21 @@ QQmlJSImporter::ImportedTypes QQmlJSImporter::importDirectory(
|
|||
{
|
||||
QHash<QString, QQmlJSScope::ConstPtr> qmlNames;
|
||||
|
||||
if (directory.startsWith(u':')) {
|
||||
if (m_mapper) {
|
||||
const auto resources = m_mapper->filter(
|
||||
QQmlJSResourceFileMapper::resourceQmlDirectoryFilter(directory.mid(1)));
|
||||
for (const auto &entry : resources) {
|
||||
const QString name = QFileInfo(entry.resourcePath).baseName();
|
||||
if (name.front().isUpper()) {
|
||||
qmlNames.insert(prefixedName(prefix, name),
|
||||
localFile2ScopeTree(entry.filePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
return qmlNames;
|
||||
}
|
||||
|
||||
QDirIterator it {
|
||||
directory,
|
||||
QStringList() << QLatin1String("*.qml"),
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
// We mean it.
|
||||
|
||||
#include "qqmljsscope_p.h"
|
||||
#include "qqmljsresourcefilemapper_p.h"
|
||||
#include <QtQml/private/qqmldirparser_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
@ -49,7 +50,13 @@ class QQmlJSImporter
|
|||
public:
|
||||
using ImportedTypes = QHash<QString, QQmlJSScope::ConstPtr>;
|
||||
|
||||
QQmlJSImporter(const QStringList &importPaths) : m_importPaths(importPaths), m_builtins({}) {}
|
||||
QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper)
|
||||
: m_importPaths(importPaths)
|
||||
, m_builtins({})
|
||||
, m_mapper(mapper)
|
||||
{}
|
||||
|
||||
QQmlJSResourceFileMapper *resourceFileMapper() { return m_mapper; }
|
||||
|
||||
ImportedTypes importBuiltins();
|
||||
ImportedTypes importQmltypes(const QStringList &qmltypesFiles);
|
||||
|
@ -113,6 +120,7 @@ private:
|
|||
QHash<QString, QQmlJSScope::Ptr> m_importedFiles;
|
||||
QList<QQmlJS::DiagnosticMessage> m_warnings;
|
||||
AvailableTypes m_builtins;
|
||||
QQmlJSResourceFileMapper *m_mapper = nullptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include "qqmljsimportvisitor_p.h"
|
||||
#include "qqmljsresourcefilemapper_p.h"
|
||||
|
||||
#include <QtCore/qfileinfo.h>
|
||||
#include <QtCore/qdir.h>
|
||||
|
@ -94,6 +95,23 @@ QQmlJSScope::Ptr QQmlJSImportVisitor::result() const
|
|||
return m_exportedRootScope;
|
||||
}
|
||||
|
||||
QString QQmlJSImportVisitor::implicitImportDirectory(
|
||||
const QString &localFile, QQmlJSResourceFileMapper *mapper)
|
||||
{
|
||||
if (mapper) {
|
||||
const auto resource = mapper->entry(
|
||||
QQmlJSResourceFileMapper::localFileFilter(localFile));
|
||||
if (resource.isValid()) {
|
||||
return resource.resourcePath.contains(u'/')
|
||||
? (u':' + resource.resourcePath.left(
|
||||
resource.resourcePath.lastIndexOf(u'/') + 1))
|
||||
: QStringLiteral(":/");
|
||||
}
|
||||
}
|
||||
|
||||
return QFileInfo(localFile).canonicalPath() + u'/';
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::importBaseModules()
|
||||
{
|
||||
Q_ASSERT(m_rootScopeImports.isEmpty());
|
||||
|
@ -315,8 +333,29 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
|
|||
auto filename = import->fileName.toString();
|
||||
if (!filename.isEmpty()) {
|
||||
const QFileInfo file(filename);
|
||||
const QFileInfo path(file.isRelative() ? QDir(m_implicitImportDirectory).filePath(filename)
|
||||
: filename);
|
||||
const QString absolute = file.isRelative()
|
||||
? QDir(m_implicitImportDirectory).filePath(filename)
|
||||
: filename;
|
||||
|
||||
if (absolute.startsWith(u':')) {
|
||||
if (m_importer->resourceFileMapper()) {
|
||||
if (m_importer->resourceFileMapper()->isFile(absolute.mid(1))) {
|
||||
const auto entry = m_importer->resourceFileMapper()->entry(
|
||||
QQmlJSResourceFileMapper::resourceFileFilter(absolute.mid(1)));
|
||||
const auto scope = m_importer->importFile(entry.filePath);
|
||||
m_rootScopeImports.insert(
|
||||
prefix.isEmpty()
|
||||
? QFileInfo(entry.resourcePath).baseName()
|
||||
: prefix,
|
||||
scope);
|
||||
} else {
|
||||
m_rootScopeImports.insert(m_importer->importDirectory(absolute, prefix));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QFileInfo path(absolute);
|
||||
if (path.isDir()) {
|
||||
m_rootScopeImports.insert(m_importer->importDirectory(path.canonicalFilePath(), prefix));
|
||||
} else if (path.isFile()) {
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct QQmlJSResourceFileMapper;
|
||||
class QQmlJSImportVisitor : public QQmlJS::AST::Visitor
|
||||
{
|
||||
public:
|
||||
|
@ -58,6 +59,9 @@ public:
|
|||
QHash<QString, QQmlJSScope::ConstPtr> imports() const { return m_rootScopeImports; }
|
||||
QHash<QString, QQmlJSScope::ConstPtr> addressableScopes() const { return m_scopesById; }
|
||||
|
||||
static QString implicitImportDirectory(
|
||||
const QString &localFile, QQmlJSResourceFileMapper *mapper);
|
||||
|
||||
protected:
|
||||
bool visit(QQmlJS::AST::UiProgram *) override;
|
||||
void endVisit(QQmlJS::AST::UiProgram *) override;
|
||||
|
|
|
@ -82,7 +82,10 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()()
|
|||
if (!rootNode)
|
||||
return errorResult();
|
||||
|
||||
QQmlJSImportVisitor membersVisitor(m_importer, QFileInfo(m_file).canonicalPath(),
|
||||
QQmlJSImportVisitor membersVisitor(
|
||||
m_importer,
|
||||
QQmlJSImportVisitor::implicitImportDirectory(
|
||||
m_file, m_importer->resourceFileMapper()),
|
||||
m_qmltypesFiles);
|
||||
rootNode->accept(&membersVisitor);
|
||||
auto result = membersVisitor.result();
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "qqmljsscope_p.h"
|
||||
#include "qqmljsimporter_p.h"
|
||||
#include "qqmljsresourcefilemapper_p.h"
|
||||
|
||||
#include <QtQml/private/qqmljsastfwd_p.h>
|
||||
#include <QtQml/private/qqmljsdiagnosticmessage_p.h>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
import QtQml
|
||||
|
||||
Simple {
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import QtQml
|
||||
|
||||
Bar {
|
||||
foo: 12
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>resource.qml</file>
|
||||
<file>badResource.qml</file>
|
||||
<file alias="Bar.qml">resource/Bar.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,5 @@
|
|||
import QtQml
|
||||
|
||||
QtObject {
|
||||
property int foo: 13
|
||||
}
|
|
@ -57,6 +57,7 @@ private Q_SLOTS:
|
|||
void qmltypes();
|
||||
|
||||
void autoqmltypes();
|
||||
void resources();
|
||||
|
||||
private:
|
||||
QString runQmllint(const QString &fileToLint,
|
||||
|
@ -183,6 +184,16 @@ void TestQmllint::autoqmltypes()
|
|||
QVERIFY(process.readAllStandardOutput().isEmpty());
|
||||
}
|
||||
|
||||
void TestQmllint::resources()
|
||||
{
|
||||
runQmllint(testFile("resource.qml"), true,
|
||||
{QStringLiteral("--resource"), testFile("resource.qrc")});
|
||||
runQmllint(testFile("badResource.qml"), false,
|
||||
{QStringLiteral("--resource"), testFile("resource.qrc")});
|
||||
runQmllint(testFile("resource.qml"), false, {});
|
||||
runQmllint(testFile("badResource.qml"), true, {});
|
||||
}
|
||||
|
||||
void TestQmllint::dirtyQmlCode_data()
|
||||
{
|
||||
QTest::addColumn<QString>("filename");
|
||||
|
|
|
@ -338,7 +338,9 @@ bool FindWarningVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
|
|||
FindWarningVisitor::FindWarningVisitor(
|
||||
QQmlJSImporter *importer, QStringList qmltypesFiles, QString code, QString fileName,
|
||||
bool silent, bool warnUnqualified, bool warnWithStatement, bool warnInheritanceCycle)
|
||||
: QQmlJSImportVisitor(importer, QFileInfo {fileName}.canonicalPath(), qmltypesFiles),
|
||||
: QQmlJSImportVisitor(importer,
|
||||
implicitImportDirectory(fileName, importer->resourceFileMapper()),
|
||||
qmltypesFiles),
|
||||
m_code(std::move(code)),
|
||||
m_rootId(QLatin1String("<id>")),
|
||||
m_filePath(std::move(fileName)),
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#include "findwarnings.h"
|
||||
|
||||
#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
|
||||
|
||||
#include <QtQml/private/qqmljslexer_p.h>
|
||||
#include <QtQml/private/qqmljsparser_p.h>
|
||||
#include <QtQml/private/qqmljsengine_p.h>
|
||||
|
@ -50,7 +52,8 @@
|
|||
|
||||
static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualified,
|
||||
const bool warnWithStatement, const bool warnInheritanceCycle,
|
||||
const QStringList &qmlImportPaths, const QStringList &qmltypesFiles)
|
||||
const QStringList &qmlImportPaths, const QStringList &qmltypesFiles,
|
||||
const QString &resourceFile)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
|
@ -85,12 +88,20 @@ static bool lint_file(const QString &filename, const bool silent, const bool war
|
|||
}
|
||||
|
||||
if (success && !isJavaScript) {
|
||||
auto root = parser.rootNode();
|
||||
QQmlJSImporter importer(qmlImportPaths);
|
||||
const auto check = [&](QQmlJSResourceFileMapper *mapper) {
|
||||
QQmlJSImporter importer(qmlImportPaths, mapper);
|
||||
FindWarningVisitor v { &importer, qmltypesFiles, code, filename, silent,
|
||||
warnUnqualified, warnWithStatement, warnInheritanceCycle };
|
||||
root->accept(&v);
|
||||
parser.rootNode()->accept(&v);
|
||||
success = v.check();
|
||||
};
|
||||
|
||||
if (resourceFile.isEmpty()) {
|
||||
check(nullptr);
|
||||
} else {
|
||||
QQmlJSResourceFileMapper mapper({ resourceFile });
|
||||
check(&mapper);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
|
@ -124,6 +135,12 @@ int main(int argv, char *argc[])
|
|||
|
||||
parser.addOption(disableCheckInheritanceCycle);
|
||||
|
||||
QCommandLineOption resourceOption(
|
||||
{ QStringLiteral("resource") },
|
||||
QStringLiteral("Look for related files in the given resource file"),
|
||||
QStringLiteral("resource"));
|
||||
parser.addOption(resourceOption);
|
||||
|
||||
QCommandLineOption qmlImportPathsOption(
|
||||
QStringList() << "I"
|
||||
<< "qmldirs",
|
||||
|
@ -177,6 +194,8 @@ int main(int argv, char *argc[])
|
|||
}
|
||||
}
|
||||
|
||||
const QString resourceFile = parser.value(resourceOption);
|
||||
|
||||
#else
|
||||
bool silent = false;
|
||||
bool warnUnqualified = true;
|
||||
|
@ -193,7 +212,7 @@ int main(int argv, char *argc[])
|
|||
for (const QString &filename : arguments)
|
||||
#endif
|
||||
success &= lint_file(filename, silent, warnUnqualified, warnWithStatement,
|
||||
warnInheritanceCycle, qmlImportPaths, qmltypesFiles);
|
||||
warnInheritanceCycle, qmlImportPaths, qmltypesFiles, resourceFile);
|
||||
|
||||
return success ? 0 : -1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue