aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler/qqmljslinter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmlcompiler/qqmljslinter.cpp')
-rw-r--r--src/qmlcompiler/qqmljslinter.cpp157
1 files changed, 95 insertions, 62 deletions
diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp
index e84b04b713..b5744b0c3d 100644
--- a/src/qmlcompiler/qqmljslinter.cpp
+++ b/src/qmlcompiler/qqmljslinter.cpp
@@ -19,6 +19,7 @@
#include <QtCore/qscopedpointer.h>
#include <QtQmlCompiler/private/qqmlsa_p.h>
+#include <QtQmlCompiler/private/qqmljsloggingutils_p.h>
#if QT_CONFIG(library)
# include <QtCore/qdiriterator.h>
@@ -169,7 +170,7 @@ bool QQmlJSLinter::Plugin::parseMetaData(const QJsonObject &metaData, QString pl
return false;
}
- QJsonObject object = value.toObject();
+ const QJsonObject object = value.toObject();
for (const QString &requiredKey : { u"name"_s, u"description"_s }) {
if (!object.contains(requiredKey)) {
@@ -179,10 +180,18 @@ bool QQmlJSLinter::Plugin::parseMetaData(const QJsonObject &metaData, QString pl
}
}
+ const auto it = object.find("enabled"_L1);
+ const bool ignored = (it != object.end() && !it->toBool());
+
const QString categoryId =
(m_isInternal ? u""_s : u"Plugin."_s) + m_name + u'.' + object[u"name"].toString();
- m_categories << QQmlJSLogger::Category { categoryId, categoryId,
- object[u"description"].toString(), QtWarningMsg };
+ const auto settingsNameIt = object.constFind(u"settingsName");
+ const QString settingsName = (settingsNameIt == object.constEnd())
+ ? categoryId
+ : settingsNameIt->toString(categoryId);
+ m_categories << QQmlJS::LoggerCategory{ categoryId, settingsName,
+ object["description"_L1].toString(), QtWarningMsg,
+ ignored };
}
return true;
@@ -190,7 +199,6 @@ bool QQmlJSLinter::Plugin::parseMetaData(const QJsonObject &metaData, QString pl
std::vector<QQmlJSLinter::Plugin> QQmlJSLinter::loadPlugins(QStringList paths)
{
-
std::vector<Plugin> plugins;
QDuplicateTracker<QString> seenPlugins;
@@ -236,7 +244,7 @@ std::vector<QQmlJSLinter::Plugin> QQmlJSLinter::loadPlugins(QStringList paths)
}
}
#endif
-
+ Q_UNUSED(paths)
return plugins;
}
@@ -265,7 +273,7 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger,
const QString category = words.at(i);
const auto categoryExists = std::any_of(
loggerCategories.cbegin(), loggerCategories.cend(),
- [&](const QQmlJSLogger::Category &cat) { return cat.id().name() == category; });
+ [&](const QQmlJS::LoggerCategory &cat) { return cat.id().name() == category; });
if (categoryExists)
categories << category;
@@ -323,7 +331,7 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger,
}
static void addJsonWarning(QJsonArray &warnings, const QQmlJS::DiagnosticMessage &message,
- QAnyStringView id, const std::optional<FixSuggestion> &suggestion = {})
+ QAnyStringView id, const std::optional<QQmlJSFixSuggestion> &suggestion = {})
{
QJsonObject jsonMessage;
@@ -362,19 +370,35 @@ static void addJsonWarning(QJsonArray &warnings, const QQmlJS::DiagnosticMessage
jsonMessage[u"message"_s] = message.message;
QJsonArray suggestions;
+ const auto convertLocation = [](const QQmlJS::SourceLocation &source, QJsonObject *target) {
+ target->insert("line"_L1, int(source.startLine));
+ target->insert("column"_L1, int(source.startColumn));
+ target->insert("charOffset"_L1, int(source.offset));
+ target->insert("length"_L1, int(source.length));
+ };
if (suggestion.has_value()) {
- for (const auto &fix : suggestion->fixes) {
- QJsonObject jsonFix;
- jsonFix[u"message"] = fix.message;
- jsonFix[u"line"_s] = static_cast<int>(fix.cutLocation.startLine);
- jsonFix[u"column"_s] = static_cast<int>(fix.cutLocation.startColumn);
- jsonFix[u"charOffset"_s] = static_cast<int>(fix.cutLocation.offset);
- jsonFix[u"length"_s] = static_cast<int>(fix.cutLocation.length);
- jsonFix[u"replacement"_s] = fix.replacementString;
- jsonFix[u"isHint"] = fix.isHint;
- if (!fix.fileName.isEmpty())
- jsonFix[u"fileName"] = fix.fileName;
- suggestions << jsonFix;
+ QJsonObject jsonFix {
+ { "message"_L1, suggestion->fixDescription() },
+ { "replacement"_L1, suggestion->replacement() },
+ { "isHint"_L1, !suggestion->isAutoApplicable() },
+ };
+ convertLocation(suggestion->location(), &jsonFix);
+ const QString filename = suggestion->filename();
+ if (!filename.isEmpty())
+ jsonFix.insert("fileName"_L1, filename);
+ suggestions << jsonFix;
+
+ const QString hint = suggestion->hint();
+ if (!hint.isEmpty()) {
+ // We need to keep compatibility with the JSON format.
+ // Therefore the overly verbose encoding of the hint.
+ QJsonObject jsonHint {
+ { "message"_L1, hint },
+ { "replacement"_L1, QString() },
+ { "isHint"_L1, true }
+ };
+ convertLocation(QQmlJS::SourceLocation(), &jsonHint);
+ suggestions << jsonHint;
}
}
jsonMessage[u"suggestions"] = suggestions;
@@ -398,7 +422,7 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
QJsonArray *json, const QStringList &qmlImportPaths,
const QStringList &qmldirFiles,
const QStringList &resourceFiles,
- const QList<QQmlJSLogger::Category> &categories)
+ const QList<QQmlJS::LoggerCategory> &categories)
{
// Make sure that we don't expose an old logger if we return before a new one is created.
m_logger.reset();
@@ -495,17 +519,17 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
if (m_enablePlugins) {
for (const Plugin &plugin : m_plugins) {
- for (const QQmlJSLogger::Category &category : plugin.categories())
+ for (const QQmlJS::LoggerCategory &category : plugin.categories())
m_logger->registerCategory(category);
}
}
for (auto it = categories.cbegin(); it != categories.cend(); ++it) {
- if (!it->changed)
+ if (auto logger = *it; !QQmlJS::LoggerCategoryPrivate::get(&logger)->hasChanged())
continue;
- m_logger->setCategoryIgnored(it->id(), it->ignored);
- m_logger->setCategoryLevel(it->id(), it->level);
+ m_logger->setCategoryIgnored(it->id(), it->isIgnored());
+ m_logger->setCategoryLevel(it->id(), it->level());
}
parseComments(m_logger.get(), engine.comments());
@@ -525,25 +549,26 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
typeResolver.init(&v, parser.rootNode());
- QQmlJSLiteralBindingCheck literalCheck;
- literalCheck.run(&v, &typeResolver);
-
- QScopedPointer<QQmlSA::PassManager> passMan;
+ using PassManagerPtr = std::unique_ptr<
+ QQmlSA::PassManager, decltype(&QQmlSA::PassManagerPrivate::deletePassManager)>;
+ PassManagerPtr passMan(QQmlSA::PassManagerPrivate::createPassManager(&v, &typeResolver),
+ &QQmlSA::PassManagerPrivate::deletePassManager);
+ passMan->registerPropertyPass(
+ std::make_unique<QQmlJSLiteralBindingCheck>(passMan.get()), QString(),
+ QString(), QString());
if (m_enablePlugins) {
- passMan.reset(new QQmlSA::PassManager(&v, &typeResolver));
-
for (const Plugin &plugin : m_plugins) {
if (!plugin.isValid() || !plugin.isEnabled())
continue;
QQmlSA::LintPlugin *instance = plugin.m_instance;
Q_ASSERT(instance);
- instance->registerPasses(passMan.get(), v.result());
+ instance->registerPasses(passMan.get(),
+ QQmlJSScope::createQQmlSAElement(v.result()));
}
-
- passMan->analyze(v.result());
}
+ passMan->analyze(QQmlJSScope::createQQmlSAElement(v.result()));
success = !m_logger->hasWarnings() && !m_logger->hasErrors();
@@ -553,16 +578,13 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
return;
}
- QQmlJSTypeInfo typeInfo;
-
const QStringList resourcePaths = mapper
? mapper->resourcePaths(QQmlJSResourceFileMapper::localFileFilter(filename))
: QStringList();
const QString resolvedPath =
(resourcePaths.size() == 1) ? u':' + resourcePaths.first() : filename;
- QQmlJSLinterCodegen codegen { &m_importer, resolvedPath, qmldirFiles, m_logger.get(),
- &typeInfo };
+ QQmlJSLinterCodegen codegen { &m_importer, resolvedPath, qmldirFiles, m_logger.get() };
codegen.setTypeResolver(std::move(typeResolver));
if (passMan)
codegen.setPassManager(passMan.get());
@@ -603,8 +625,9 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
return success ? LintSuccess : HasWarnings;
}
-QQmlJSLinter::LintResult QQmlJSLinter::lintModule(const QString &module, const bool silent,
- QJsonArray *json)
+QQmlJSLinter::LintResult QQmlJSLinter::lintModule(
+ const QString &module, const bool silent, QJsonArray *json,
+ const QStringList &qmlImportPaths, const QStringList &resourceFiles)
{
// Make sure that we don't expose an old logger if we return before a new one is created.
m_logger.reset();
@@ -612,6 +635,15 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintModule(const QString &module, const b
// We can't lint properly if a module has already been pre-cached
m_importer.clearCache();
+ if (m_importer.importPaths() != qmlImportPaths)
+ m_importer.setImportPaths(qmlImportPaths);
+
+ QQmlJSResourceFileMapper mapper(resourceFiles);
+ if (!resourceFiles.isEmpty())
+ m_importer.setResourceFileMapper(&mapper);
+ else
+ m_importer.setResourceFileMapper(nullptr);
+
QJsonArray warnings;
QJsonObject result;
@@ -768,7 +800,7 @@ QQmlJSLinter::FixResult QQmlJSLinter::applyFixes(QString *fixedCode, bool silent
QString code = m_fileContents;
- QList<FixSuggestion::Fix> fixesToApply;
+ QList<QQmlJSFixSuggestion> fixesToApply;
QFileInfo info(m_logger->fileName());
const QString currentFileAbsolutePath = info.absoluteFilePath();
@@ -782,34 +814,33 @@ QQmlJSLinter::FixResult QQmlJSLinter::applyFixes(QString *fixedCode, bool silent
for (const auto &messages : { m_logger->infos(), m_logger->warnings(), m_logger->errors() })
for (const Message &msg : messages) {
- if (!msg.fixSuggestion.has_value())
+ if (!msg.fixSuggestion.has_value() || !msg.fixSuggestion->isAutoApplicable())
continue;
- for (const auto &fix : msg.fixSuggestion->fixes) {
- if (fix.isHint)
- continue;
-
- // Ignore fix suggestions for other files
- if (!fix.fileName.isEmpty()
- && QFileInfo(fix.fileName).absoluteFilePath() != currentFileAbsolutePath) {
- continue;
- }
-
- fixesToApply << fix;
+ // Ignore fix suggestions for other files
+ const QString filename = msg.fixSuggestion->filename();
+ if (!filename.isEmpty()
+ && QFileInfo(filename).absoluteFilePath() != currentFileAbsolutePath) {
+ continue;
}
+
+ fixesToApply << msg.fixSuggestion.value();
}
if (fixesToApply.isEmpty())
return NothingToFix;
std::sort(fixesToApply.begin(), fixesToApply.end(),
- [](FixSuggestion::Fix &a, FixSuggestion::Fix &b) {
- return a.cutLocation.offset < b.cutLocation.offset;
+ [](const QQmlJSFixSuggestion &a, const QQmlJSFixSuggestion &b) {
+ return a.location().offset < b.location().offset;
});
+ const auto dupes = std::unique(fixesToApply.begin(), fixesToApply.end());
+ fixesToApply.erase(dupes, fixesToApply.end());
+
for (auto it = fixesToApply.begin(); it + 1 != fixesToApply.end(); it++) {
- QQmlJS::SourceLocation srcLocA = it->cutLocation;
- QQmlJS::SourceLocation srcLocB = (it + 1)->cutLocation;
+ const QQmlJS::SourceLocation srcLocA = it->location();
+ const QQmlJS::SourceLocation srcLocB = (it + 1)->location();
if (srcLocA.offset + srcLocA.length > srcLocB.offset) {
if (!silent)
qWarning() << "Fixes for two warnings are overlapping, aborting. Please file a bug "
@@ -821,12 +852,14 @@ QQmlJSLinter::FixResult QQmlJSLinter::applyFixes(QString *fixedCode, bool silent
int offsetChange = 0;
for (const auto &fix : fixesToApply) {
- qsizetype cutLocation = fix.cutLocation.offset + offsetChange;
- QString before = code.left(cutLocation);
- QString after = code.mid(cutLocation + fix.cutLocation.length);
-
- code = before + fix.replacementString + after;
- offsetChange += fix.replacementString.size() - fix.cutLocation.length;
+ const QQmlJS::SourceLocation fixLocation = fix.location();
+ qsizetype cutLocation = fixLocation.offset + offsetChange;
+ const QString before = code.left(cutLocation);
+ const QString after = code.mid(cutLocation + fixLocation.length);
+
+ const QString replacement = fix.replacement();
+ code = before + replacement + after;
+ offsetChange += replacement.size() - fixLocation.length;
}
QQmlJS::Engine engine;