qmllint: Remove exceptions for most unknown builtins

All those types are properly defined in the qmltypes files now. We just
need to search the enumerations the same way as methods and properties
in order to find everything.

Also, deduplicate the code that resolves properties, methods, and enums
by using a common template for iterating the scopes.

Change-Id: I0bf1423974d0ec8f602ecd0342522b3e981a8586
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2021-01-20 14:20:48 +01:00
parent 90be89d771
commit 39944e8467
6 changed files with 99 additions and 87 deletions

View File

@ -298,7 +298,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
qmlEnum.addKey(member->member.toString());
qmlEnum.addValue(int(member->value));
}
m_currentScope->addEnumeration(qmlEnum);
m_currentScope->addOwnEnumeration(qmlEnum);
return true;
}

View File

@ -38,6 +38,30 @@
QT_BEGIN_NAMESPACE
template<typename Action>
static bool searchBaseAndExtensionTypes(const QQmlJSScope *type, const Action &check)
{
const QQmlJSScope *nonCompositeBase = nullptr;
for (const QQmlJSScope *scope = type; scope; scope = scope->baseType().data()) {
if (check(scope))
return true;
if (!nonCompositeBase && !scope->isComposite())
nonCompositeBase = scope;
}
if (!nonCompositeBase)
return false;
for (const QQmlJSScope *scope = nonCompositeBase->extensionType().data(); scope;
scope = scope->baseType().data()) {
if (check(scope))
return true;
}
return false;
}
QQmlJSScope::QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope)
: m_parentScope(parentScope), m_scopeType(type) {}
@ -78,41 +102,55 @@ bool QQmlJSScope::isIdInCurrentScope(const QString &id) const
bool QQmlJSScope::hasMethod(const QString &name) const
{
const QQmlJSScope *nonCompositeBase = isComposite() ? this : nullptr;
for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().data()) {
if (scope->m_methods.contains(name))
return true;
if (!nonCompositeBase && !scope->isComposite())
nonCompositeBase = scope;
}
if (nonCompositeBase && nonCompositeBase != this) {
if (QQmlJSScope::ConstPtr extension = nonCompositeBase->extensionType())
return extension->hasMethod(name);
}
return false;
return searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
return scope->m_methods.contains(name);
});
}
QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name) const
{
QList<QQmlJSMetaMethod> results;
const QQmlJSScope *nonCompositeBase = isComposite() ? this : nullptr;
for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().data()) {
searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
results.append(scope->ownMethods(name));
if (!nonCompositeBase && !scope->isComposite())
nonCompositeBase = scope;
}
if (nonCompositeBase && nonCompositeBase != this) {
if (QQmlJSScope::ConstPtr extension = nonCompositeBase->extensionType())
results.append(extension->methods(name));
}
return false;
});
return results;
}
bool QQmlJSScope::hasEnumeration(const QString &name) const
{
return searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
return scope->m_enumerations.contains(name);
});
}
bool QQmlJSScope::hasEnumerationKey(const QString &name) const
{
return searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
for (const auto &e : scope->m_enumerations) {
if (e.keys().contains(name))
return true;
}
return false;
});
}
QQmlJSMetaEnum QQmlJSScope::enumeration(const QString &name) const
{
QQmlJSMetaEnum result;
searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
const auto it = scope->m_enumerations.find(name);
if (it == scope->m_enumerations.end())
return false;
result = *it;
return true;
});
return result;
}
bool QQmlJSScope::isIdInCurrentQmlScopes(const QString &id) const
{
if (m_scopeType == QQmlJSScope::QMLScope)
@ -262,41 +300,22 @@ void QQmlJSScope::addExport(const QString &name, const QString &package,
bool QQmlJSScope::hasProperty(const QString &name) const
{
const QQmlJSScope *nonCompositeBase = isComposite() ? this : nullptr;
for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().data()) {
if (scope->m_properties.contains(name))
return true;
if (!nonCompositeBase && !scope->isComposite())
nonCompositeBase = scope;
}
if (nonCompositeBase && nonCompositeBase != this) {
if (QQmlJSScope::ConstPtr extension = nonCompositeBase->extensionType())
return extension->hasProperty(name);
}
return false;
return searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
return scope->m_properties.contains(name);
});
}
QQmlJSMetaProperty QQmlJSScope::property(const QString &name) const
{
const QQmlJSScope *nonCompositeBase = isComposite() ? this : nullptr;
for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().data()) {
QQmlJSMetaProperty prop;
searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
const auto it = scope->m_properties.find(name);
if (it != scope->m_properties.end())
return *it;
if (!nonCompositeBase && !scope->isComposite())
nonCompositeBase = scope;
}
if (nonCompositeBase && nonCompositeBase != this) {
if (QQmlJSScope::ConstPtr extension = nonCompositeBase->extensionType())
return extension->property(name);
}
return {};
if (it == scope->m_properties.end())
return false;
prop = *it;
return true;
});
return prop;
}
QQmlJSScope::Export::Export(QString package, QString type, const QTypeRevision &version) :

View File

@ -175,13 +175,14 @@ public:
bool hasMethod(const QString &name) const;
QList<QQmlJSMetaMethod> methods(const QString &name) const;
void addEnumeration(const QQmlJSMetaEnum &enumeration)
{
m_enumerations.insert(enumeration.name(), enumeration);
}
QHash<QString, QQmlJSMetaEnum> enumerations() const { return m_enumerations; }
QQmlJSMetaEnum enumeration(const QString &name) const { return m_enumerations.value(name); }
bool hasEnumeration(const QString &name) const { return m_enumerations.contains(name); }
void addOwnEnumeration(const QQmlJSMetaEnum &enumeration) { m_enumerations.insert(enumeration.name(), enumeration); }
QHash<QString, QQmlJSMetaEnum> ownEnumerations() const { return m_enumerations; }
QQmlJSMetaEnum ownEnumeration(const QString &name) const { return m_enumerations.value(name); }
bool hasOwnEnumeration(const QString &name) const { return m_enumerations.contains(name); }
bool hasEnumeration(const QString &name) const;
bool hasEnumerationKey(const QString &name) const;
QQmlJSMetaEnum enumeration(const QString &name) const;
QString fileName() const { return m_fileName; }
void setFileName(const QString &file) { m_fileName = file; }

View File

@ -390,7 +390,7 @@ void QQmlJSTypeDescriptionReader::readEnum(UiObjectDefinition *ast, const QQmlJS
}
}
scope->addEnumeration(metaEnum);
scope->addOwnEnumeration(metaEnum);
}
void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSMetaMethod *metaMethod)

View File

@ -1,5 +1,7 @@
import QtQml
import "." as MyStuff
MyStuff.Simple {
property bool something: contains(Qt.point(12, 34))
property int other: Qt.AlignBottom
}

View File

@ -56,10 +56,7 @@ private:
};
static const QStringList unknownBuiltins = {
// TODO: "string" should be added to builtins.qmltypes, and the special handling below removed
QStringLiteral("alias"), // TODO: we cannot properly resolve aliases, yet
QStringLiteral("QRectF"), // TODO: should be added to builtins.qmltypes
QStringLiteral("QFont"), // TODO: should be added to builtins.qmltypes
QStringLiteral("QJSValue"), // We cannot say anything intelligent about untyped JS values.
QStringLiteral("variant"), // Same for generic variants
};
@ -208,22 +205,19 @@ bool CheckIdentifiers::checkMemberAccess(const QVector<FieldMember> &members,
return true; // Access to property of JS function
auto checkEnums = [&](const QQmlJSScope::ConstPtr &scope) {
const auto enums = scope->enumerations();
for (const auto &enumerator : enums) {
if (enumerator.name() == access.m_name) {
detectedRestrictiveKind = QLatin1String("enum");
detectedRestrictiveName = access.m_name;
expectedNext.append(enumerator.keys());
return true;
}
for (const QString &key : enumerator.keys()) {
if (access.m_name == key) {
detectedRestrictiveKind = QLatin1String("enum");
detectedRestrictiveName = access.m_name;
return true;
}
}
if (scope->hasEnumeration(access.m_name)) {
detectedRestrictiveKind = QLatin1String("enum");
detectedRestrictiveName = access.m_name;
expectedNext.append(scope->enumeration(access.m_name).keys());
return true;
}
if (scope->hasEnumerationKey(access.m_name)) {
detectedRestrictiveKind = QLatin1String("enum");
detectedRestrictiveName = access.m_name;
return true;
}
return false;
};
@ -360,10 +354,6 @@ bool CheckIdentifiers::operator()(
continue;
}
// TODO: Lots of builtins are missing
if (memberAccessBase.m_name == QLatin1String("Qt"))
continue;
const auto typeIt = m_types.find(memberAccessBase.m_name);
if (typeIt != m_types.end()) {
if (typeIt->isNull()) {
@ -389,7 +379,7 @@ bool CheckIdentifiers::operator()(
const auto firstElement = root->childScopes()[0];
if (firstElement->hasProperty(memberAccessBase.m_name)
|| firstElement->hasMethod(memberAccessBase.m_name)
|| firstElement->enumerations().contains(memberAccessBase.m_name)) {
|| firstElement->hasEnumeration(memberAccessBase.m_name)) {
m_colorOut->writePrefixedMessage(
memberAccessBase.m_name
+ QLatin1String(" is a member of the root element\n")