QmlCompiler: Introduce grouped scopes

This way we can analyze the left hand part of things like
"anchors.fill: parent" in qmllint.

Change-Id: I0f58312566c3d5062e0fb301c2bad908ab8b8cbb
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2020-10-21 16:06:20 +02:00
parent 23813a3198
commit ed8127cf41
6 changed files with 85 additions and 8 deletions

View File

@ -51,7 +51,10 @@ void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QS
const QQmlJS::SourceLocation &location)
{
m_currentScope = QQmlJSScope::create(type, m_currentScope);
m_currentScope->setBaseTypeName(name);
if (type == QQmlJSScope::GroupedPropertyScope)
m_currentScope->setInternalName(name);
else
m_currentScope->setBaseTypeName(name);
m_currentScope->setIsComposite(true);
m_currentScope->setSourceLocation(location);
}
@ -126,6 +129,7 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
void QQmlJSImportVisitor::endVisit(UiObjectDefinition *)
{
m_currentScope->resolveGroupedScopes();
leaveEnvironment();
}
@ -235,11 +239,27 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
{
if (scriptBinding->qualifiedId->name == QLatin1String("id")) {
const auto id = scriptBinding->qualifiedId;
if (!id->next && id->name == QLatin1String("id")) {
const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
const auto *idExprension = cast<IdentifierExpression *>(statement->expression);
m_scopesById.insert(idExprension->name.toString(), m_currentScope);
} else {
for (auto group = id; group->next; group = group->next) {
const QString name = group->name.toString();
if (name.isEmpty() || name.front().isUpper())
break; // TODO: uppercase grouped scopes are attached properties. Handle them.
enterEnvironment(QQmlJSScope::GroupedPropertyScope, name, group->firstSourceLocation());
}
// TODO: remember the actual binding, once we can process it.
while (m_currentScope->scopeType() == QQmlJSScope::GroupedPropertyScope)
leaveEnvironment();
}
return true;
}
@ -448,6 +468,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
{
m_currentScope->resolveGroupedScopes();
const QQmlJSScope::ConstPtr childScope = m_currentScope;
leaveEnvironment();

View File

@ -168,6 +168,27 @@ void QQmlJSScope::resolveTypes(const QHash<QString, QQmlJSScope::ConstPtr> &cont
}
}
void QQmlJSScope::resolveGroupedScopes()
{
for (auto it = m_childScopes.begin(), end = m_childScopes.end(); it != end; ++it) {
QQmlJSScope::Ptr childScope = *it;
if (childScope->scopeType() != QQmlJSScope::GroupedPropertyScope)
continue;
const QString propertyName = childScope->internalName();
for (const QQmlJSScope *type = this; type; type = type->baseType().data()) {
auto propertyIt = type->m_properties.find(propertyName);
if (propertyIt != type->m_properties.end()) {
childScope->m_baseType = QQmlJSScope::ConstPtr(propertyIt->type());
childScope->m_baseTypeName = propertyIt->typeName();
break;
}
}
childScope->resolveGroupedScopes();
}
}
QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
{
auto qmlScope = scope;

View File

@ -94,7 +94,8 @@ public:
{
JSFunctionScope,
JSLexicalScope,
QMLScope
QMLScope,
GroupedPropertyScope
};
enum class AccessSemantics {
@ -221,6 +222,7 @@ public:
}
void resolveTypes(const QHash<QString, ConstPtr> &contextualTypes);
void resolveGroupedScopes();
void setSourceLocation(const QQmlJS::SourceLocation &sourceLocation)
{

View File

@ -222,11 +222,11 @@ void TestQmllint::dirtyQmlCode_data()
<< QString();
QTest::newRow("nanchors2")
<< QStringLiteral("nanchors2.qml")
<< QString()
<< QString("unknown grouped property scope nanchors.")
<< QString();
QTest::newRow("nanchors3")
<< QStringLiteral("nanchors3.qml")
<< QString()
<< QString("unknown grouped property scope nanchors.")
<< QString();
QTest::newRow("badAliasObject")
<< QStringLiteral("badAliasObject.qml")
@ -246,9 +246,7 @@ void TestQmllint::dirtyQmlCode()
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
QEXPECT_FAIL("anchors3", "We don't see that QQuickItem cannot be assigned to QQuickAnchorLine", Abort);
QEXPECT_FAIL("nanchors1", "Invalid grouped properties are not detected", Abort);
QEXPECT_FAIL("nanchors2", "Invalid grouped properties are not detected", Abort);
QEXPECT_FAIL("nanchors3", "Invalid grouped properties are not detected", Abort);
QEXPECT_FAIL("nanchors1", "Invalid grouped properties are not always detected", Abort);
QVERIFY(process.exitCode() != 0);
});

View File

@ -90,6 +90,28 @@ void FindWarningVisitor::checkInheritanceCycle(QQmlJSScope::ConstPtr scope)
}
}
void FindWarningVisitor::checkGroupedScopes(QQmlJSScope::ConstPtr scope)
{
auto children = scope->childScopes();
while (!children.isEmpty()) {
auto childScope = children.takeFirst();
if (childScope->scopeType() != QQmlJSScope::GroupedPropertyScope)
continue;
if (!childScope->baseType()) {
m_errors.append({
QStringLiteral("unknown grouped property scope %1.")
.arg(childScope->internalName()),
QtWarningMsg,
childScope->sourceLocation()
});
m_visitFailed = true;
}
children.append(childScope->childScopes());
}
}
void FindWarningVisitor::flushPendingSignalParameters()
{
const SignalHandler handler = m_signalHandlers[m_pendingSingalHandler];
@ -349,6 +371,14 @@ bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
return true;
}
void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
{
QQmlJSImportVisitor::endVisit(uiob);
if (m_warnUnqualified)
checkGroupedScopes(m_currentScope);
}
bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
{
using namespace QQmlJS::AST;
@ -433,6 +463,9 @@ void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *uiod)
auto childScope = m_currentScope;
QQmlJSImportVisitor::endVisit(uiod);
if (m_warnUnqualified)
checkGroupedScopes(childScope);
if (m_currentScope == m_globalScope
|| m_currentScope->baseTypeName() == QStringLiteral("Component")) {
return;

View File

@ -91,6 +91,7 @@ private:
QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered
void checkInheritanceCycle(QQmlJSScope::ConstPtr scope);
void checkGroupedScopes(QQmlJSScope::ConstPtr scope);
void flushPendingSignalParameters();
void throwRecursionDepthError() override;
@ -108,6 +109,7 @@ private:
/* --- end block handling --- */
bool visit(QQmlJS::AST::UiObjectBinding *uiob) override;
void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override;
bool visit(QQmlJS::AST::UiObjectDefinition *uiod) override;
void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
bool visit(QQmlJS::AST::UiScriptBinding *uisb) override;