aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2022-02-24 10:28:52 +0100
committerMaximilian Goldstein <max.goldstein@qt.io>2022-03-03 11:19:51 +0100
commit195af3ce69600076782224d9fe74f3ca597ad70f (patch)
tree8bf0cbaf2810467cd3d6386fe33254196cf52884
parent0fc8a511baa6493c8d80046dd99b8eba3634d2a2 (diff)
qmlcompiler: Allow for logging other files and add auto-fix infrastructure
This change makes sure the logger can write messages and show excerpts from files other than the current one. This is necessary in order to give users hints about problems that arise due to other components. It also adds a field to FixSuggestion::Fix which specifies whether a suggested fix can be applied automatically or is only a hint. Change-Id: I94a929d3fc3fc966591cffb99e67309d264c38e7 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qmlcompiler/qqmljslinter.cpp1
-rw-r--r--src/qmlcompiler/qqmljslogger.cpp60
-rw-r--r--src/qmlcompiler/qqmljslogger_p.h18
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp13
4 files changed, 68 insertions, 24 deletions
diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp
index 91182e8f60..4d3eb8e9b5 100644
--- a/src/qmlcompiler/qqmljslinter.cpp
+++ b/src/qmlcompiler/qqmljslinter.cpp
@@ -221,6 +221,7 @@ bool QQmlJSLinter::lintFile(const QString &filename, const QString *fileContents
jsonFix[u"charOffset"_qs] = static_cast<int>(fix.cutLocation.offset);
jsonFix[u"length"_qs] = static_cast<int>(fix.cutLocation.length);
jsonFix[u"replacement"_qs] = fix.replacementString;
+ jsonFix[u"isHint"] = fix.isHint;
suggestions << jsonFix;
}
}
diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp
index cba6a24d60..ce2f559c1a 100644
--- a/src/qmlcompiler/qqmljslogger.cpp
+++ b/src/qmlcompiler/qqmljslogger.cpp
@@ -28,6 +28,9 @@
#include "qqmljslogger_p.h"
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+
QT_BEGIN_NAMESPACE
const QMap<QString, QQmlJSLogger::Option> &QQmlJSLogger::options() {
@@ -133,7 +136,8 @@ static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
- bool showFileName, const std::optional<FixSuggestion> &suggestion)
+ bool showFileName, const std::optional<FixSuggestion> &suggestion,
+ const QString overrideFileName)
{
if (isCategoryIgnored(category))
return;
@@ -145,8 +149,9 @@ void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
QString prefix;
- if (!m_fileName.isEmpty() && showFileName)
- prefix = m_fileName + QStringLiteral(":");
+ if ((!overrideFileName.isEmpty() || !m_fileName.isEmpty()) && showFileName)
+ prefix =
+ (!overrideFileName.isEmpty() ? overrideFileName : m_fileName) + QStringLiteral(":");
if (srcLocation.isValid())
prefix += QStringLiteral("%1:%2:").arg(srcLocation.startLine).arg(srcLocation.startColumn);
@@ -176,7 +181,7 @@ void QQmlJSLogger::log(const QString &message, QQmlJSLoggerCategory category,
}
if (srcLocation.isValid() && !m_code.isEmpty() && showContext)
- printContext(srcLocation);
+ printContext(overrideFileName, srcLocation);
if (suggestion.has_value())
printFix(suggestion.value());
@@ -198,9 +203,19 @@ void QQmlJSLogger::processMessages(const QList<QQmlJS::DiagnosticMessage> &messa
m_output.write(QStringLiteral("---\n\n"));
}
-void QQmlJSLogger::printContext(const QQmlJS::SourceLocation &location)
+void QQmlJSLogger::printContext(const QString &overrideFileName,
+ const QQmlJS::SourceLocation &location)
{
- IssueLocationWithContext issueLocationWithContext { m_code, location };
+ QString code = m_code;
+
+ if (!overrideFileName.isEmpty() && overrideFileName != QFileInfo(m_fileName).absolutePath()) {
+ QFile file(overrideFileName);
+ const bool success = file.open(QFile::ReadOnly);
+ Q_ASSERT(success);
+ code = QString::fromUtf8(file.readAll());
+ }
+
+ IssueLocationWithContext issueLocationWithContext { code, location };
if (const QStringView beforeText = issueLocationWithContext.beforeText(); !beforeText.isEmpty())
m_output.write(beforeText);
@@ -224,33 +239,56 @@ void QQmlJSLogger::printContext(const QQmlJS::SourceLocation &location)
void QQmlJSLogger::printFix(const FixSuggestion &fix)
{
+ const QString currentFileAbsPath = QFileInfo(m_fileName).absolutePath();
+ QString code = m_code;
+ QString currentFile;
for (const auto &fixItem : fix.fixes) {
m_output.writePrefixedMessage(fixItem.message, QtInfoMsg);
if (!fixItem.cutLocation.isValid())
continue;
- IssueLocationWithContext issueLocationWithContext { m_code, fixItem.cutLocation };
+ if (fixItem.fileName == currentFile) {
+ // Nothing to do in this case, we've already read the code
+ } else if (fixItem.fileName.isEmpty() || fixItem.fileName == currentFileAbsPath) {
+ code = m_code;
+ } else {
+ QFile file(fixItem.fileName);
+ const bool success = file.open(QFile::ReadOnly);
+ Q_ASSERT(success);
+ code = QString::fromUtf8(file.readAll());
+ currentFile = fixItem.fileName;
+ }
+
+ IssueLocationWithContext issueLocationWithContext { code, fixItem.cutLocation };
if (const QStringView beforeText = issueLocationWithContext.beforeText();
!beforeText.isEmpty()) {
m_output.write(beforeText);
}
- m_output.write(fixItem.replacementString, QtDebugMsg);
+ // The replacement string can be empty if we're only pointing something out to the user
+ QStringView replacementString = fixItem.replacementString.isEmpty()
+ ? issueLocationWithContext.issueText()
+ : fixItem.replacementString;
+
+ // But if there's nothing to change it has to be a hint
+ if (fixItem.replacementString.isEmpty())
+ Q_ASSERT(fixItem.isHint);
+
+ m_output.write(replacementString, QtDebugMsg);
m_output.write(issueLocationWithContext.afterText().toString() + u'\n');
int tabCount = issueLocationWithContext.beforeText().count(u'\t');
// Do not draw location indicator for multiline replacement strings
- if (fixItem.replacementString.contains(u'\n'))
+ if (replacementString.contains(u'\n'))
continue;
m_output.write(u" "_qs.repeated(
issueLocationWithContext.beforeText().length() - tabCount)
+ u"\t"_qs.repeated(tabCount)
- + u"^"_qs.repeated(fixItem.replacementString.length())
- + u'\n');
+ + u"^"_qs.repeated(fixItem.replacementString.length()) + u'\n');
}
}
diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h
index 462854225f..2ddcb2ac5a 100644
--- a/src/qmlcompiler/qqmljslogger_p.h
+++ b/src/qmlcompiler/qqmljslogger_p.h
@@ -122,6 +122,10 @@ struct FixSuggestion
QString message;
QQmlJS::SourceLocation cutLocation = QQmlJS::SourceLocation();
QString replacementString = QString();
+ QString fileName = QString();
+ // A Fix is a hint if it can not be automatically applied to fix an issue or only points out
+ // its origin
+ bool isHint = true;
};
QList<Fix> fixes;
};
@@ -233,10 +237,11 @@ public:
*/
void log(const QString &message, QQmlJSLoggerCategory category,
const QQmlJS::SourceLocation &srcLocation, bool showContext = true,
- bool showFileName = true, const std::optional<FixSuggestion> &suggestion = {})
+ bool showFileName = true, const std::optional<FixSuggestion> &suggestion = {},
+ const QString overrideFileName = QString())
{
log(message, category, srcLocation, m_categoryLevels[category], showContext, showFileName,
- suggestion);
+ suggestion, overrideFileName);
}
void processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
@@ -257,12 +262,13 @@ public:
QString fileName() const { return m_fileName; }
private:
- void printContext(const QQmlJS::SourceLocation &location);
+ void printContext(const QString &overrideFileName, const QQmlJS::SourceLocation &location);
void printFix(const FixSuggestion &fix);
- void log(const QString &message, QQmlJSLoggerCategory category, const QQmlJS::SourceLocation &,
- QtMsgType type, bool showContext, bool showFileName,
- const std::optional<FixSuggestion> &suggestion);
+ void log(const QString &message, QQmlJSLoggerCategory category,
+ const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
+ bool showFileName, const std::optional<FixSuggestion> &suggestion,
+ const QString overrideFileName);
QString m_fileName;
QString m_code;
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 52dd8df72f..fb7c40cf7a 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -340,7 +340,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM
"function instead.\n")
.arg(id.location.startLine)
.arg(id.location.startColumn),
- fixLocation, fixString
+ fixLocation, fixString, QString(), false
};
}
break;
@@ -360,7 +360,7 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM
name + QLatin1String(" is a member of a parent element\n")
+ QLatin1String(" You can qualify the access with its id "
"to avoid this warning:\n"),
- fixLocation, (id.isEmpty() ? u"<id>."_qs : (id + u'.'))
+ fixLocation, (id.isEmpty() ? u"<id>."_qs : (id + u'.')), QString(), id.isEmpty()
};
if (id.isEmpty()) {
@@ -677,11 +677,10 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
QQmlJS::SourceLocation fixLocation = getCurrentSourceLocation();
fixLocation.length = 0;
- suggestion.fixes << FixSuggestion::Fix {
- u"Reference it by id instead:"_qs,
- fixLocation,
- id.isEmpty() ? u"<id>."_qs : (id + u'.')
- };
+ suggestion.fixes << FixSuggestion::Fix { u"Reference it by id instead:"_qs,
+ fixLocation,
+ id.isEmpty() ? u"<id>."_qs : (id + u'.'),
+ QString(), id.isEmpty() };
fixLocation = scope->sourceLocation();
fixLocation.length = 0;