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;
|
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 {
|
QDirIterator it {
|
||||||
directory,
|
directory,
|
||||||
QStringList() << QLatin1String("*.qml"),
|
QStringList() << QLatin1String("*.qml"),
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
// We mean it.
|
// We mean it.
|
||||||
|
|
||||||
#include "qqmljsscope_p.h"
|
#include "qqmljsscope_p.h"
|
||||||
|
#include "qqmljsresourcefilemapper_p.h"
|
||||||
#include <QtQml/private/qqmldirparser_p.h>
|
#include <QtQml/private/qqmldirparser_p.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
@ -49,7 +50,13 @@ class QQmlJSImporter
|
||||||
public:
|
public:
|
||||||
using ImportedTypes = QHash<QString, QQmlJSScope::ConstPtr>;
|
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 importBuiltins();
|
||||||
ImportedTypes importQmltypes(const QStringList &qmltypesFiles);
|
ImportedTypes importQmltypes(const QStringList &qmltypesFiles);
|
||||||
|
@ -113,6 +120,7 @@ private:
|
||||||
QHash<QString, QQmlJSScope::Ptr> m_importedFiles;
|
QHash<QString, QQmlJSScope::Ptr> m_importedFiles;
|
||||||
QList<QQmlJS::DiagnosticMessage> m_warnings;
|
QList<QQmlJS::DiagnosticMessage> m_warnings;
|
||||||
AvailableTypes m_builtins;
|
AvailableTypes m_builtins;
|
||||||
|
QQmlJSResourceFileMapper *m_mapper = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "qqmljsimportvisitor_p.h"
|
#include "qqmljsimportvisitor_p.h"
|
||||||
|
#include "qqmljsresourcefilemapper_p.h"
|
||||||
|
|
||||||
#include <QtCore/qfileinfo.h>
|
#include <QtCore/qfileinfo.h>
|
||||||
#include <QtCore/qdir.h>
|
#include <QtCore/qdir.h>
|
||||||
|
@ -94,6 +95,23 @@ QQmlJSScope::Ptr QQmlJSImportVisitor::result() const
|
||||||
return m_exportedRootScope;
|
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()
|
void QQmlJSImportVisitor::importBaseModules()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_rootScopeImports.isEmpty());
|
Q_ASSERT(m_rootScopeImports.isEmpty());
|
||||||
|
@ -315,8 +333,29 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiImport *import)
|
||||||
auto filename = import->fileName.toString();
|
auto filename = import->fileName.toString();
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
const QFileInfo file(filename);
|
const QFileInfo file(filename);
|
||||||
const QFileInfo path(file.isRelative() ? QDir(m_implicitImportDirectory).filePath(filename)
|
const QString absolute = file.isRelative()
|
||||||
: filename);
|
? 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()) {
|
if (path.isDir()) {
|
||||||
m_rootScopeImports.insert(m_importer->importDirectory(path.canonicalFilePath(), prefix));
|
m_rootScopeImports.insert(m_importer->importDirectory(path.canonicalFilePath(), prefix));
|
||||||
} else if (path.isFile()) {
|
} else if (path.isFile()) {
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
struct QQmlJSResourceFileMapper;
|
||||||
class QQmlJSImportVisitor : public QQmlJS::AST::Visitor
|
class QQmlJSImportVisitor : public QQmlJS::AST::Visitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -58,6 +59,9 @@ public:
|
||||||
QHash<QString, QQmlJSScope::ConstPtr> imports() const { return m_rootScopeImports; }
|
QHash<QString, QQmlJSScope::ConstPtr> imports() const { return m_rootScopeImports; }
|
||||||
QHash<QString, QQmlJSScope::ConstPtr> addressableScopes() const { return m_scopesById; }
|
QHash<QString, QQmlJSScope::ConstPtr> addressableScopes() const { return m_scopesById; }
|
||||||
|
|
||||||
|
static QString implicitImportDirectory(
|
||||||
|
const QString &localFile, QQmlJSResourceFileMapper *mapper);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool visit(QQmlJS::AST::UiProgram *) override;
|
bool visit(QQmlJS::AST::UiProgram *) override;
|
||||||
void endVisit(QQmlJS::AST::UiProgram *) override;
|
void endVisit(QQmlJS::AST::UiProgram *) override;
|
||||||
|
|
|
@ -82,8 +82,11 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()()
|
||||||
if (!rootNode)
|
if (!rootNode)
|
||||||
return errorResult();
|
return errorResult();
|
||||||
|
|
||||||
QQmlJSImportVisitor membersVisitor(m_importer, QFileInfo(m_file).canonicalPath(),
|
QQmlJSImportVisitor membersVisitor(
|
||||||
m_qmltypesFiles);
|
m_importer,
|
||||||
|
QQmlJSImportVisitor::implicitImportDirectory(
|
||||||
|
m_file, m_importer->resourceFileMapper()),
|
||||||
|
m_qmltypesFiles);
|
||||||
rootNode->accept(&membersVisitor);
|
rootNode->accept(&membersVisitor);
|
||||||
auto result = membersVisitor.result();
|
auto result = membersVisitor.result();
|
||||||
result->setInternalName(scopeName);
|
result->setInternalName(scopeName);
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
|
|
||||||
#include "qqmljsscope_p.h"
|
#include "qqmljsscope_p.h"
|
||||||
#include "qqmljsimporter_p.h"
|
#include "qqmljsimporter_p.h"
|
||||||
|
#include "qqmljsresourcefilemapper_p.h"
|
||||||
|
|
||||||
#include <QtQml/private/qqmljsastfwd_p.h>
|
#include <QtQml/private/qqmljsastfwd_p.h>
|
||||||
#include <QtQml/private/qqmljsdiagnosticmessage_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 qmltypes();
|
||||||
|
|
||||||
void autoqmltypes();
|
void autoqmltypes();
|
||||||
|
void resources();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString runQmllint(const QString &fileToLint,
|
QString runQmllint(const QString &fileToLint,
|
||||||
|
@ -183,6 +184,16 @@ void TestQmllint::autoqmltypes()
|
||||||
QVERIFY(process.readAllStandardOutput().isEmpty());
|
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()
|
void TestQmllint::dirtyQmlCode_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("filename");
|
QTest::addColumn<QString>("filename");
|
||||||
|
|
|
@ -338,7 +338,9 @@ bool FindWarningVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
|
||||||
FindWarningVisitor::FindWarningVisitor(
|
FindWarningVisitor::FindWarningVisitor(
|
||||||
QQmlJSImporter *importer, QStringList qmltypesFiles, QString code, QString fileName,
|
QQmlJSImporter *importer, QStringList qmltypesFiles, QString code, QString fileName,
|
||||||
bool silent, bool warnUnqualified, bool warnWithStatement, bool warnInheritanceCycle)
|
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_code(std::move(code)),
|
||||||
m_rootId(QLatin1String("<id>")),
|
m_rootId(QLatin1String("<id>")),
|
||||||
m_filePath(std::move(fileName)),
|
m_filePath(std::move(fileName)),
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#include "findwarnings.h"
|
#include "findwarnings.h"
|
||||||
|
|
||||||
|
#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
|
||||||
|
|
||||||
#include <QtQml/private/qqmljslexer_p.h>
|
#include <QtQml/private/qqmljslexer_p.h>
|
||||||
#include <QtQml/private/qqmljsparser_p.h>
|
#include <QtQml/private/qqmljsparser_p.h>
|
||||||
#include <QtQml/private/qqmljsengine_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,
|
static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualified,
|
||||||
const bool warnWithStatement, const bool warnInheritanceCycle,
|
const bool warnWithStatement, const bool warnInheritanceCycle,
|
||||||
const QStringList &qmlImportPaths, const QStringList &qmltypesFiles)
|
const QStringList &qmlImportPaths, const QStringList &qmltypesFiles,
|
||||||
|
const QString &resourceFile)
|
||||||
{
|
{
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
if (!file.open(QFile::ReadOnly)) {
|
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) {
|
if (success && !isJavaScript) {
|
||||||
auto root = parser.rootNode();
|
const auto check = [&](QQmlJSResourceFileMapper *mapper) {
|
||||||
QQmlJSImporter importer(qmlImportPaths);
|
QQmlJSImporter importer(qmlImportPaths, mapper);
|
||||||
FindWarningVisitor v { &importer, qmltypesFiles, code, filename, silent,
|
FindWarningVisitor v { &importer, qmltypesFiles, code, filename, silent,
|
||||||
warnUnqualified, warnWithStatement, warnInheritanceCycle };
|
warnUnqualified, warnWithStatement, warnInheritanceCycle };
|
||||||
root->accept(&v);
|
parser.rootNode()->accept(&v);
|
||||||
success = v.check();
|
success = v.check();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (resourceFile.isEmpty()) {
|
||||||
|
check(nullptr);
|
||||||
|
} else {
|
||||||
|
QQmlJSResourceFileMapper mapper({ resourceFile });
|
||||||
|
check(&mapper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
@ -124,6 +135,12 @@ int main(int argv, char *argc[])
|
||||||
|
|
||||||
parser.addOption(disableCheckInheritanceCycle);
|
parser.addOption(disableCheckInheritanceCycle);
|
||||||
|
|
||||||
|
QCommandLineOption resourceOption(
|
||||||
|
{ QStringLiteral("resource") },
|
||||||
|
QStringLiteral("Look for related files in the given resource file"),
|
||||||
|
QStringLiteral("resource"));
|
||||||
|
parser.addOption(resourceOption);
|
||||||
|
|
||||||
QCommandLineOption qmlImportPathsOption(
|
QCommandLineOption qmlImportPathsOption(
|
||||||
QStringList() << "I"
|
QStringList() << "I"
|
||||||
<< "qmldirs",
|
<< "qmldirs",
|
||||||
|
@ -177,6 +194,8 @@ int main(int argv, char *argc[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString resourceFile = parser.value(resourceOption);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
bool silent = false;
|
bool silent = false;
|
||||||
bool warnUnqualified = true;
|
bool warnUnqualified = true;
|
||||||
|
@ -193,7 +212,7 @@ int main(int argv, char *argc[])
|
||||||
for (const QString &filename : arguments)
|
for (const QString &filename : arguments)
|
||||||
#endif
|
#endif
|
||||||
success &= lint_file(filename, silent, warnUnqualified, warnWithStatement,
|
success &= lint_file(filename, silent, warnUnqualified, warnWithStatement,
|
||||||
warnInheritanceCycle, qmlImportPaths, qmltypesFiles);
|
warnInheritanceCycle, qmlImportPaths, qmltypesFiles, resourceFile);
|
||||||
|
|
||||||
return success ? 0 : -1;
|
return success ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue