aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h4
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp123
-rw-r--r--src/qmlcompiler/qqmljstypepropagator_p.h19
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp1
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h9
5 files changed, 147 insertions, 9 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index 7073d90edf..7ed406300a 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -67,6 +67,10 @@ public:
QHash<QString, QQmlJSScope::ConstPtr> imports() const { return m_rootScopeImports; }
QHash<QString, QQmlJSScope::ConstPtr> addressableScopes() const { return m_scopesById; }
+ QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> signalHandlers() const
+ {
+ return m_signalHandlers;
+ }
static QString implicitImportDirectory(
const QString &localFile, QQmlJSResourceFileMapper *mapper);
diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp
index 6d3a2b4a7c..87e8d33167 100644
--- a/src/qmlcompiler/qqmljstypepropagator.cpp
+++ b/src/qmlcompiler/qqmljstypepropagator.cpp
@@ -42,10 +42,13 @@ QQmlJSTypePropagator::~QQmlJSTypePropagator() { }
QQmlJSTypePropagator::TypePropagationResult QQmlJSTypePropagator::propagateTypes(
const QV4::Compiler::Context *context, const QQmlJS::AST::BoundNames &arguments,
const QQmlJSScope::ConstPtr &returnType, const QQmlJSScope::ConstPtr &qmlScope,
- bool isSignalHandler, Error *error)
+ const QHash<QString, QQmlJSScope::ConstPtr> &addressableScopes, bool isSignalHandler,
+ Error *error)
{
m_error = error;
m_currentScope = qmlScope;
+ m_currentContext = context;
+ m_addressableScopes = addressableScopes;
m_arguments = arguments;
m_returnType = m_typeResolver->globalType(returnType);
m_isSignalHandler = isSignalHandler;
@@ -234,13 +237,127 @@ void QQmlJSTypePropagator::generate_LoadGlobalLookup(int index)
generate_LoadName(m_jsUnitGenerator->lookupNameIndex(index));
}
+void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name)
+{
+ Q_ASSERT(m_currentContext->sourceLocationTable);
+ const auto &entries = m_currentContext->sourceLocationTable->entries;
+
+ auto item = std::lower_bound(entries.begin(), entries.end(), currentInstructionOffset(),
+ [](auto entry, uint offset) { return entry.offset < offset; });
+ Q_ASSERT(item != entries.end());
+ auto location = item->location;
+
+ if (m_currentScope->isInCustomParserParent()) {
+ Q_ASSERT(!m_currentScope->baseType().isNull());
+ // Only ignore custom parser based elements if it's not Connections.
+ if (m_currentScope->baseType()->internalName() != u"QQmlConnections"_qs)
+ return;
+ }
+
+ m_logger->logWarning(QLatin1String("Unqualified access"), Log_UnqualifiedAccess, location);
+
+ auto childScopes = m_currentScope->childScopes();
+ for (qsizetype i = 0; i < m_currentScope->childScopes().length(); i++) {
+ auto &scope = childScopes[i];
+ if (location.offset > scope->sourceLocation().offset) {
+ if (i + 1 < childScopes.length()
+ && childScopes.at(i + 1)->sourceLocation().offset < location.offset)
+ continue;
+ if (scope->childScopes().length() == 0)
+ continue;
+
+ const auto jsId = scope->childScopes().first()->findJSIdentifier(name);
+
+ if (jsId.has_value() && jsId->kind == QQmlJSScope::JavaScriptIdentifier::Injected) {
+
+ FixSuggestion suggestion { Log_UnqualifiedAccess, QtInfoMsg, {} };
+
+ const QQmlJSScope::JavaScriptIdentifier id = jsId.value();
+
+ QQmlJS::SourceLocation fixLocation = id.location;
+ Q_UNUSED(fixLocation)
+ fixLocation.length = 0;
+
+ const auto handler = m_typeResolver->signalHandlers()[id.location];
+
+ QString fixString = handler.isMultiline ? u" function("_qs : u" ("_qs;
+ const auto parameters = handler.signalParameters;
+ for (int numParams = parameters.size(); numParams > 0; --numParams) {
+ fixString += parameters.at(parameters.size() - numParams);
+ if (numParams > 1)
+ fixString += u", "_qs;
+ }
+
+ fixString += handler.isMultiline ? u") "_qs : u") => "_qs;
+
+ suggestion.fixes << FixSuggestion::Fix {
+ name
+ + QString::fromLatin1(" is accessible in this scope because "
+ "you are handling a signal at %1:%2\n")
+ .arg(id.location.startLine)
+ .arg(id.location.startColumn),
+ QtInfoMsg, fixLocation, fixString
+ };
+
+ m_logger->suggestFix(suggestion);
+ }
+ break;
+ }
+ }
+
+ for (QQmlJSScope::ConstPtr scope = m_currentScope; !scope.isNull();
+ scope = scope->parentScope()) {
+ if (scope->hasProperty(name)) {
+ auto it = std::find_if(m_addressableScopes.constBegin(), m_addressableScopes.constEnd(),
+ [&](const QQmlJSScope::ConstPtr ptr) { return ptr == scope; });
+
+ FixSuggestion suggestion { Log_UnqualifiedAccess, QtInfoMsg, {} };
+
+ QQmlJS::SourceLocation fixLocation = location;
+ fixLocation.length = 0;
+
+ QString id = it == m_addressableScopes.constEnd() ? u"<id>"_qs : it.key();
+
+ suggestion.fixes << FixSuggestion::Fix {
+ name + QLatin1String(" is a member of a parent element\n")
+ + QLatin1String(" You can qualify the access with its id "
+ "to avoid this warning:\n"),
+ QtInfoMsg, fixLocation, id + u"."_qs
+ };
+
+ if (it == m_addressableScopes.constEnd()) {
+ suggestion.fixes << FixSuggestion::Fix {
+ u"You first have to give the element an id"_qs,
+ QtInfoMsg,
+ QQmlJS::SourceLocation {},
+ {}
+ };
+ }
+
+ m_logger->suggestFix(suggestion);
+ }
+ }
+}
+
void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index)
{
const QString name =
m_jsUnitGenerator->stringForIndex(m_jsUnitGenerator->lookupNameIndex(index));
- m_state.accumulatorOut = m_typeResolver->scopedType(m_currentScope, name);
+ m_state.accumulatorOut = m_typeResolver->scopedType(m_currentScope, m_state.savedPrefix + name);
+
+ if (!m_state.accumulatorOut.isValid() && m_typeResolver->isPrefix(name)) {
+ m_state.savedPrefix = name + u"."_qs;
+ m_state.accumulatorOut = m_state.accumulatorIn.isValid()
+ ? m_state.accumulatorIn
+ : m_typeResolver->globalType(m_currentScope);
+ return;
+ }
+
+ m_state.savedPrefix.clear();
+
if (!m_state.accumulatorOut.isValid()) {
- setError(u"Cannot access value for name "_qs + name);
+ setError(u"Cannot access value for name "_qs + name, true);
+ handleUnqualifiedAccess(name);
} else if (m_typeResolver->genericType(m_state.accumulatorOut.storedType()).isNull()) {
// It should really be valid.
// We get the generic type from aotContext->loadQmlContextPropertyIdLookup().
diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h
index e8e64711ca..055958672f 100644
--- a/src/qmlcompiler/qqmljstypepropagator_p.h
+++ b/src/qmlcompiler/qqmljstypepropagator_p.h
@@ -61,6 +61,7 @@ struct QQmlJSTypePropagator : public QV4::Moth::ByteCodeHandler
{
QString message;
int instructionOffset;
+ bool hasLoggerMessage;
bool isSet() const { return !message.isEmpty(); }
};
@@ -72,11 +73,11 @@ struct QQmlJSTypePropagator : public QV4::Moth::ByteCodeHandler
using TypePropagationResult = QHash<int, InstructionAnnotation>;
- TypePropagationResult propagateTypes(const QV4::Compiler::Context *context,
- const QQmlJS::AST::BoundNames &arguments,
- const QQmlJSScope::ConstPtr &returnType,
- const QQmlJSScope::ConstPtr &qmlScope,
- bool isSignalHandler, Error *error);
+ TypePropagationResult
+ propagateTypes(const QV4::Compiler::Context *context, const QQmlJS::AST::BoundNames &arguments,
+ const QQmlJSScope::ConstPtr &returnType, const QQmlJSScope::ConstPtr &qmlScope,
+ const QHash<QString, QQmlJSScope::ConstPtr> &addressableScopes,
+ bool isSignalHandler, Error *error);
void generate_Ret() override;
void generate_Debug() override;
@@ -223,9 +224,10 @@ struct QQmlJSTypePropagator : public QV4::Moth::ByteCodeHandler
};
private:
- void setError(const QString &message)
+ void setError(const QString &message, bool hasLoggerMessage = false)
{
m_error->message = message;
+ m_error->hasLoggerMessage = hasLoggerMessage;
m_error->instructionOffset = currentInstructionOffset();
}
@@ -243,6 +245,7 @@ private:
QQmlJSVirtualRegisters registers;
QQmlJSRegisterContent accumulatorIn;
QQmlJSRegisterContent accumulatorOut;
+ QString savedPrefix;
QHash<int, InstructionAnnotation> annotations;
@@ -257,6 +260,8 @@ private:
bool needsMorePasses = false;
};
+ void handleUnqualifiedAccess(const QString &name);
+
void propagateBinaryOperation(QSOperator::Op op, int lhs);
void propagateCall(const QList<QQmlJSMetaMethod> &methods, int argc, int argv);
void propagatePropertyLookup(const QString &name);
@@ -283,6 +288,8 @@ private:
QV4::Compiler::JSUnitGenerator *m_jsUnitGenerator = nullptr;
QQmlJSScope::ConstPtr m_currentScope;
+ const QV4::Compiler::Context *m_currentContext;
+ QHash<QString, QQmlJSScope::ConstPtr> m_addressableScopes;
QQmlJSLogger *m_logger;
// Not part of the state, as the back jumps are the reason for running multiple passes
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp
index 1735560a70..5c3b6db435 100644
--- a/src/qmlcompiler/qqmljstyperesolver.cpp
+++ b/src/qmlcompiler/qqmljstyperesolver.cpp
@@ -100,6 +100,7 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(
QQueue<QQmlJSScope::Ptr> objects;
objects.enqueue(visitor.result());
m_objectsById = visitor.addressableScopes();
+ m_signalHandlers = visitor.signalHandlers();
while (!objects.isEmpty()) {
const QQmlJSScope::Ptr object = objects.dequeue();
diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h
index 239352e33b..c7dddd45b1 100644
--- a/src/qmlcompiler/qqmljstyperesolver_p.h
+++ b/src/qmlcompiler/qqmljstyperesolver_p.h
@@ -95,6 +95,8 @@ public:
QQmlJSScope::ConstPtr scopeForLocation(const QV4::CompiledData::Location &location) const;
QQmlJSScope::ConstPtr scopeForId(const QString &id) const;
+ bool isPrefix(const QString &name) { return m_imports.contains(name) && !m_imports[name]; }
+
QQmlJSScope::ConstPtr typeForName(const QString &name) const { return m_imports[name]; }
QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const;
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const;
@@ -136,6 +138,12 @@ public:
storedType(const QQmlJSScope::ConstPtr &type,
ComponentIsGeneric allowComponent = ComponentIsGeneric::No) const;
+ const QHash<QString, QQmlJSScope::ConstPtr> &objectsById() { return m_objectsById; }
+ const QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> &signalHandlers()
+ {
+ return m_signalHandlers;
+ }
+
private:
QQmlJSScope::ConstPtr merge(const QQmlJSScope::ConstPtr &a,
const QQmlJSScope::ConstPtr &b) const;
@@ -171,6 +179,7 @@ private:
QHash<QString, QQmlJSScope::ConstPtr> m_objectsById;
QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> m_objectsByLocation;
QHash<QString, QQmlJSScope::ConstPtr> m_imports;
+ QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> m_signalHandlers;
TypeStorage m_typeStorage = Direct;
Semantics m_semantics = Dynamic;