diff options
Diffstat (limited to 'src/qmlcompiler/qqmljslinter.cpp')
-rw-r--r-- | src/qmlcompiler/qqmljslinter.cpp | 157 |
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; |