qqmlsa: Make property pass check base and extension types as well

In many cases we need to also check base an extension types since
we don't always have direct inheritance
(i.e. very common with QtQuick.Controls)

Change-Id: I66307b7d0081d49611a9e61847e4363d5819bf82
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Maximilian Goldstein 2022-05-20 11:53:40 +02:00
parent 80f0bf64e4
commit 15efc5c323
3 changed files with 54 additions and 25 deletions

View File

@ -97,6 +97,12 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run(
void QQmlJSTypePropagator::generate_Ret()
{
if (m_passManager != nullptr && m_function->isProperty) {
m_passManager->analyzeBinding(m_function->qmlScope,
m_typeResolver->containedType(m_state.accumulatorIn()),
getCurrentBindingSourceLocation());
}
if (m_function->isSignalHandler) {
// Signal handlers cannot return anything.
} else if (!m_returnType.isValid() && m_state.accumulatorIn().isValid()
@ -125,12 +131,6 @@ void QQmlJSTypePropagator::generate_Ret()
addReadAccumulator(m_returnType);
}
if (m_passManager != nullptr && m_function->isProperty) {
m_passManager->analyzeBinding(m_function->qmlScope,
m_typeResolver->containedType(m_state.accumulatorIn()),
getCurrentBindingSourceLocation());
}
m_state.setHasSideEffects(true);
m_state.skipInstructionsUntilNextJumpTarget = true;
}

View File

@ -32,6 +32,7 @@
#include "qqmljslogger_p.h"
#include "qqmljstyperesolver_p.h"
#include "qqmljsimportvisitor_p.h"
#include "qqmljsutils_p.h"
#include <memory>
@ -101,7 +102,7 @@ static QString lookupName(const QQmlSA::Element &element, LookupMode mode = Look
bool PassManager::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
QAnyStringView moduleName, QAnyStringView typeName,
QAnyStringView propertyName)
QAnyStringView propertyName, bool allowInheritance)
{
QString name;
if (!moduleName.isEmpty() && !typeName.isEmpty()) {
@ -114,7 +115,11 @@ bool PassManager::registerPropertyPass(std::shared_ptr<PropertyPass> pass,
name = lookupName(element, Register);
}
m_propertyPasses.insert({ std::make_pair<>(name, propertyName.toString()), std::move(pass) });
const PassManager::PropertyPassInfo passInfo {
propertyName.isEmpty() ? QStringList {} : QStringList { propertyName.toString() },
std::move(pass), allowInheritance
};
m_propertyPasses.insert({ name, passInfo });
return true;
}
@ -195,7 +200,7 @@ void PassManager::analyzeBinding(const Element &element, const QQmlSA::Element &
for (PropertyPass *pass : findPropertyUsePasses(element, propertyName))
pass->onBinding(element, propertyName, binding, bindingScope, value);
if (!info->second.isAttached)
if (!info->second.isAttached || bindingScope->baseType().isNull())
return;
for (PropertyPass *pass : findPropertyUsePasses(bindingScope->baseType(), propertyName))
@ -207,20 +212,35 @@ bool PassManager::hasImportedModule(QAnyStringView module) const
return m_visitor->imports().contains(u"$module$." + module.toString());
}
std::vector<PropertyPass *> PassManager::findPropertyUsePasses(const QQmlSA::Element &element,
const QString &propertyName)
QSet<PropertyPass *> PassManager::findPropertyUsePasses(const QQmlSA::Element &element,
const QString &propertyName)
{
const QString typeName = lookupName(element);
std::vector<PropertyPass *> passes;
for (const auto &key :
{ std::make_pair<>(typeName, propertyName), std::make_pair<>(QString(), propertyName),
std::make_pair<>(typeName, QString()) }) {
auto pass = m_propertyPasses.equal_range(key);
if (pass.first == pass.second)
continue;
QStringList typeNames { lookupName(element) };
for (auto it = pass.first; it != pass.second; it++)
passes.push_back(it->second.get());
QQmlJSUtils::searchBaseAndExtensionTypes(
element, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
Q_UNUSED(mode);
typeNames.append(lookupName(scope));
return false;
});
QSet<PropertyPass *> passes;
for (const QString &typeName : typeNames) {
for (auto &pass :
{ m_propertyPasses.equal_range(u""_s), m_propertyPasses.equal_range(typeName) }) {
if (pass.first == pass.second)
continue;
for (auto it = pass.first; it != pass.second; it++) {
if (typeName != typeNames.constFirst() && !it->second.allowInheritance)
continue;
if (it->second.properties.isEmpty()
|| it->second.properties.contains(propertyName)) {
passes.insert(it->second.pass.get());
}
}
}
}
return passes;
}

View File

@ -42,6 +42,7 @@
#include <qtqmlcompilerexports.h>
#include <private/qqmljsscope_p.h>
#include <QtCore/qset.h>
#include <map>
#include <unordered_map>
@ -126,7 +127,8 @@ public:
void registerElementPass(std::unique_ptr<ElementPass> pass);
bool registerPropertyPass(std::shared_ptr<PropertyPass> pass, QAnyStringView moduleName,
QAnyStringView typeName,
QAnyStringView propertyName = QAnyStringView());
QAnyStringView propertyName = QAnyStringView(),
bool allowInheritance = true);
void analyze(const Element &root);
bool hasImportedModule(QAnyStringView name) const;
@ -134,8 +136,8 @@ public:
private:
friend struct ::QQmlJSTypePropagator;
std::vector<PropertyPass *> findPropertyUsePasses(const QQmlSA::Element &element,
const QString &propertyName);
QSet<PropertyPass *> findPropertyUsePasses(const QQmlSA::Element &element,
const QString &propertyName);
void analyzeWrite(const QQmlSA::Element &element, QString propertyName,
const QQmlSA::Element &value, const QQmlSA::Element &writeScope,
@ -153,12 +155,19 @@ private:
bool isAttached;
};
struct PropertyPassInfo
{
QStringList properties;
std::shared_ptr<PropertyPass> pass;
bool allowInheritance = true;
};
void addBindingSourceLocations(const QQmlSA::Element &element,
const QQmlSA::Element &scope = QQmlSA::Element(),
const QString prefix = QString(), bool isAttached = false);
std::vector<std::unique_ptr<ElementPass>> m_elementPasses;
std::multimap<std::pair<QString, QString>, std::shared_ptr<PropertyPass>> m_propertyPasses;
std::multimap<QString, PropertyPassInfo> m_propertyPasses;
std::unordered_map<quint32, BindingInfo> m_bindingsByLocation;
QQmlJSImportVisitor *m_visitor;
QQmlJSTypeResolver *m_typeResolver;