diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor_p.h | 4 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 123 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljstypepropagator_p.h | 19 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 1 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 9 |
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; |
