qmlls: use resource files, when available
When linting via qmllint, the resource files are used to map from source folders to buildfolders and are needed to load qmldir files from the build folder, for example. The linting module of qmlls was not loading qmldir, for example, because it did not pass the resource folders to the linting method. This is the reason why qmldir imports, for example, were not working in qmlls. Find the resource .qrc files from the build folder and let qmlls pass them to the linting methods. Also fix the import folders to discover in-source-directory qmldir files, when no qmldir file was found in the build folder, in the same way as qmllint does it. Add some tests for both cases (qmldir in build directory and qmldir in the source directory). Fixes: QTBUG-111429 Change-Id: I885c962cac5b1276f12bc28d6d47c43212f853e4 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
f95f070e5a
commit
d0fcb75aab
|
@ -2,6 +2,9 @@
|
|||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include "qqmldom_utils_p.h"
|
||||
#include <QtCore/qdir.h>
|
||||
#include <QtCore/qdiriterator.h>
|
||||
#include <QtCore/qstring.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
@ -10,6 +13,25 @@ Q_LOGGING_CATEGORY(QQmlJSDomImporting, "qt.qqmljsdom.importing")
|
|||
namespace QQmlJS {
|
||||
namespace Dom {
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
QStringList resourceFilesFromBuildFolders(const QStringList &buildFolders)
|
||||
{
|
||||
QStringList result;
|
||||
for (const QString &path : buildFolders) {
|
||||
QDir dir(path);
|
||||
if (!dir.cd(u".rcc"_s))
|
||||
continue;
|
||||
|
||||
QDirIterator it(dir.canonicalPath(), QStringList{ u"*.qrc"_s }, QDir::Files,
|
||||
QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
result.append(it.next());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Dom
|
||||
}; // namespace QQmlJS
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include "qqmldomitem_p.h"
|
||||
#include <QtCore/qstringlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
@ -34,7 +35,7 @@ namespace QQmlJS {
|
|||
namespace Dom {
|
||||
|
||||
void createDom(MutableDomItem qmlFile, DomCreationOptions options = None);
|
||||
|
||||
QStringList resourceFilesFromBuildFolders(const QStringList &buildFolders);
|
||||
}
|
||||
}; // namespace QQmlJS
|
||||
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
#include "qqmldomastdumper_p.h"
|
||||
#include "qqmldomattachedinfo_p.h"
|
||||
#include "qqmldomastcreator_p.h"
|
||||
#include "qqmldom_utils_p.h"
|
||||
|
||||
#include <QtQml/private/qqmljsast_p.h>
|
||||
#include <QtQmlCompiler/private/qqmljsutils_p.h>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
@ -280,17 +282,19 @@ bool QQmlDomAstCreator::visit(UiProgram *program)
|
|||
// we hide the component span because the component s written after the imports
|
||||
FileLocations::addRegion(rootMap, QString(), combineLocations(program));
|
||||
pushEl(p, *cPtr, program);
|
||||
// implicit imports
|
||||
|
||||
// add implicit directory import
|
||||
if (!fInfo.canonicalPath().isEmpty()) {
|
||||
Import selfDirImport(QmlUri::fromDirectoryString(fInfo.canonicalPath()));
|
||||
selfDirImport.implicit = true;
|
||||
qmlFilePtr->addImport(selfDirImport);
|
||||
}
|
||||
// add implicit imports from the environment (QML, QtQml for example)
|
||||
for (Import i : qmlFile.environment().ownerAs<DomEnvironment>()->implicitImports()) {
|
||||
i.implicit = true;
|
||||
qmlFilePtr->addImport(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2118,10 +2122,11 @@ static QStringList qmldirFilesFrom(MutableDomItem &qmlFile)
|
|||
}
|
||||
|
||||
QQmlDomAstCreatorWithQQmlJSScope::QQmlDomAstCreatorWithQQmlJSScope(MutableDomItem &qmlFile,
|
||||
QQmlJSLogger *logger)
|
||||
QQmlJSLogger *logger,
|
||||
QQmlJSResourceFileMapper *mapper)
|
||||
: m_root(QQmlJSScope::create()),
|
||||
m_logger(logger),
|
||||
m_importer(importPathsFrom(qmlFile), nullptr, true),
|
||||
m_importer(importPathsFrom(qmlFile), mapper, true),
|
||||
m_implicitImportDirectory(QQmlJSImportVisitor::implicitImportDirectory(
|
||||
m_logger->fileName(), m_importer.resourceFileMapper())),
|
||||
m_scopeCreator(m_root, &m_importer, m_logger, m_implicitImportDirectory,
|
||||
|
@ -2226,10 +2231,19 @@ void createDom(MutableDomItem qmlFile, DomCreationOptions options)
|
|||
{
|
||||
if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
|
||||
QQmlJSLogger logger; // TODO
|
||||
|
||||
std::unique_ptr<QQmlJSResourceFileMapper> mapper;
|
||||
if (auto environmentPtr = qmlFile.environment().ownerAs<DomEnvironment>()) {
|
||||
const QStringList resourceFiles =
|
||||
resourceFilesFromBuildFolders(environmentPtr->loadPaths());
|
||||
mapper = std::make_unique<QQmlJSResourceFileMapper>(
|
||||
resourceFilesFromBuildFolders(resourceFiles));
|
||||
}
|
||||
// the logger filename is used to populate the QQmlJSScope filepath.
|
||||
logger.setFileName(qmlFile.canonicalFilePath());
|
||||
if (options.testFlag(DomCreationOption::WithSemanticAnalysis)) {
|
||||
auto v = std::make_unique<QQmlDomAstCreatorWithQQmlJSScope>(qmlFile, &logger);
|
||||
auto v = std::make_unique<QQmlDomAstCreatorWithQQmlJSScope>(qmlFile, &logger,
|
||||
mapper.get());
|
||||
v->enableScriptExpressions(options.testFlag(DomCreationOption::WithScriptExpressions));
|
||||
|
||||
AST::Node::accept(qmlFilePtr->ast(), v.get());
|
||||
|
|
|
@ -447,7 +447,8 @@ public:
|
|||
class QQmlDomAstCreatorWithQQmlJSScope : public AST::Visitor
|
||||
{
|
||||
public:
|
||||
QQmlDomAstCreatorWithQQmlJSScope(MutableDomItem &qmlFile, QQmlJSLogger *logger);
|
||||
QQmlDomAstCreatorWithQQmlJSScope(MutableDomItem &qmlFile, QQmlJSLogger *logger,
|
||||
QQmlJSResourceFileMapper *mapper);
|
||||
|
||||
#define X(name) \
|
||||
bool visit(AST::name *) override; \
|
||||
|
|
|
@ -63,7 +63,7 @@ ErrorGroups QmldirFile::myParsingErrors()
|
|||
return res;
|
||||
}
|
||||
|
||||
std::shared_ptr<QmldirFile> QmldirFile::fromPathAndCode(QString path, QString code)
|
||||
std::shared_ptr<QmldirFile> QmldirFile::fromPathAndCode(const QString &path, const QString &code)
|
||||
{
|
||||
QString canonicalFilePath = QFileInfo(path).canonicalFilePath();
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ public:
|
|||
}
|
||||
QmldirFile(const QmldirFile &o) = default;
|
||||
|
||||
static std::shared_ptr<QmldirFile> fromPathAndCode(QString path, QString code);
|
||||
static std::shared_ptr<QmldirFile> fromPathAndCode(const QString &path, const QString &code);
|
||||
|
||||
std::shared_ptr<QmldirFile> makeCopy(DomItem &self) const
|
||||
{
|
||||
|
|
|
@ -7,9 +7,12 @@
|
|||
#include <QtQmlCompiler/private/qqmljslinter_p.h>
|
||||
#include <QtQmlCompiler/private/qqmljslogger_p.h>
|
||||
#include <QtQmlDom/private/qqmldom_utils_p.h>
|
||||
#include <QtQmlDom/private/qqmldomtop_p.h>
|
||||
#include <QtCore/qlibraryinfo.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qfileinfo.h>
|
||||
#include <QtCore/qdir.h>
|
||||
#include <chrono>
|
||||
|
||||
using namespace QLspSpecification;
|
||||
|
@ -295,12 +298,16 @@ void QmlLintSuggestions::diagnoseHelper(const QByteArray &url,
|
|||
qCDebug(lintLog) << "has doc, do real lint";
|
||||
QStringList imports = m_codeModel->buildPathsForFileUrl(url);
|
||||
imports.append(QLibraryInfo::path(QLibraryInfo::QmlImportsPath));
|
||||
const QString filename = doc.canonicalFilePath();
|
||||
// add source directory as last import as fallback in case there is no qmldir in the build
|
||||
// folder this mimics qmllint behaviors
|
||||
imports.append(QFileInfo(filename).dir().absolutePath());
|
||||
// add m_server->clientInfo().rootUri & co?
|
||||
bool silent = true;
|
||||
QString filename = doc.canonicalFilePath();
|
||||
QString fileContents = doc.field(Fields::code).value().toString();
|
||||
QStringList qmltypesFiles;
|
||||
QStringList resourceFiles;
|
||||
const QString fileContents = doc.field(Fields::code).value().toString();
|
||||
const QStringList qmltypesFiles;
|
||||
const QStringList resourceFiles = resourceFilesFromBuildFolders(imports);
|
||||
|
||||
QList<QQmlJS::LoggerCategory> categories;
|
||||
|
||||
QQmlJSLinter linter(imports);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import QtQuick
|
||||
|
||||
Item {
|
||||
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
module BuildDir
|
||||
BuildDirType 1.0 BuildDirType.qml
|
||||
import QtQuick.Controls.Basic
|
||||
|
|
|
@ -6,4 +6,5 @@ BuildDirType {
|
|||
width: 250
|
||||
height: 10
|
||||
}
|
||||
Button {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import QtQuick
|
||||
|
||||
Item {
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import QtQuick
|
||||
|
||||
Item {
|
||||
B {}
|
||||
Button {}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
module SourceDir
|
||||
B 1.0 A.qml
|
||||
import QtQuick.Controls.Basic
|
|
@ -1342,6 +1342,61 @@ void tst_qmlls_modules::rangeFormatting()
|
|||
QTRY_VERIFY_WITH_TIMEOUT(*didFinish, 10000);
|
||||
}
|
||||
|
||||
void tst_qmlls_modules::qmldirImportsFromBuild()
|
||||
{
|
||||
const QString filePath = u"completions/fromBuildDir.qml"_s;
|
||||
const auto uri = openFile(filePath);
|
||||
QVERIFY(uri);
|
||||
|
||||
Notifications::AddBuildDirsParams bDirs;
|
||||
UriToBuildDirs ub;
|
||||
ub.baseUri = *uri;
|
||||
ub.buildDirs.append(testFile("buildDir").toUtf8());
|
||||
bDirs.buildDirsToSet.append(ub);
|
||||
m_protocol->typedRpc()->sendNotification(QByteArray(Notifications::AddBuildDirsMethod), bDirs);
|
||||
|
||||
bool diagnosticOk = false;
|
||||
m_protocol->registerPublishDiagnosticsNotificationHandler(
|
||||
[&diagnosticOk, &uri](const QByteArray &, const PublishDiagnosticsParams &p) {
|
||||
if (p.uri != *uri)
|
||||
return;
|
||||
|
||||
if constexpr (enable_debug_output) {
|
||||
for (const auto &x : p.diagnostics) {
|
||||
qDebug() << x.message;
|
||||
}
|
||||
}
|
||||
QCOMPARE(p.diagnostics.size(), 0);
|
||||
diagnosticOk = true;
|
||||
});
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk, 5000);
|
||||
}
|
||||
|
||||
void tst_qmlls_modules::qmldirImportsFromSource()
|
||||
{
|
||||
const QString filePath = u"sourceDir/Main.qml"_s;
|
||||
const auto uri = openFile(filePath);
|
||||
QVERIFY(uri);
|
||||
|
||||
bool diagnosticOk = false;
|
||||
m_protocol->registerPublishDiagnosticsNotificationHandler(
|
||||
[&diagnosticOk, &uri](const QByteArray &, const PublishDiagnosticsParams &p) {
|
||||
if (p.uri != *uri)
|
||||
return;
|
||||
|
||||
if constexpr (enable_debug_output) {
|
||||
for (const auto &x : p.diagnostics) {
|
||||
qDebug() << x.message;
|
||||
}
|
||||
}
|
||||
QCOMPARE(p.diagnostics.size(), 0);
|
||||
diagnosticOk = true;
|
||||
});
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(diagnosticOk, 5000);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qmlls_modules)
|
||||
|
||||
#include <tst_qmlls_modules.moc>
|
||||
|
|
|
@ -60,6 +60,8 @@ private slots:
|
|||
void linting();
|
||||
void rangeFormatting_data();
|
||||
void rangeFormatting();
|
||||
void qmldirImportsFromBuild();
|
||||
void qmldirImportsFromSource();
|
||||
|
||||
private:
|
||||
QProcess m_server;
|
||||
|
|
Loading…
Reference in New Issue