Fix error messages when assigning to non-existent properties in new compiler
authorSimon Hausmann <simon.hausmann@digia.com>
Mon, 30 Sep 2013 06:37:37 +0000 (08:37 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 30 Sep 2013 16:21:45 +0000 (18:21 +0200)
Introduce a simple valdator pass early on to catch those assignments. Also
fix storing the correct line/col for default property object bindings and
remember the minor/major version of an import in the final type reference.

Change-Id: Ib2a93dfe1a30fcd9c09b5443fb8199ad11b19769
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/compiler/qqmlcodegenerator.cpp
src/qml/qml/qqmlcompiler_p.h
src/qml/qml/qqmlobjectcreator.cpp
src/qml/qml/qqmlobjectcreator_p.h
src/qml/qml/qqmltypeloader.cpp

index ca4d8ec..84bab99 100644 (file)
@@ -197,7 +197,7 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node)
     bool isType = lastId->name.unicode()->isUpper();
     if (isType) {
         int idx = defineQMLObject(node);
-        appendBinding(AST::SourceLocation(), emptyStringIndex, idx);
+        appendBinding(node->qualifiedTypeNameId->identifierToken, emptyStringIndex, idx);
     } else {
         int idx = defineQMLObject(/*qualfied type name id*/0, node->initializer);
         appendBinding(node->qualifiedTypeNameId, idx);
index 475e438..b0fad47 100644 (file)
@@ -101,12 +101,18 @@ public:
     struct TypeReference 
     {
         TypeReference()
-        : type(0), typePropertyCache(0), component(0) {}
+            : type(0), typePropertyCache(0), component(0)
+            , majorVersion(0)
+            , minorVersion(0)
+        {}
 
         QQmlType *type;
         QQmlPropertyCache *typePropertyCache;
         QQmlCompiledData *component;
 
+        int majorVersion;
+        int minorVersion;
+
         QQmlPropertyCache *propertyCache() const;
         QQmlPropertyCache *createPropertyCache(QQmlEngine *);
     };
index c320ca9..8cc0246 100644 (file)
@@ -1025,12 +1025,7 @@ bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingI
             return false;
     }
 
-    // Child item:
-    // ...
-    //    Item {
-    //        ...
-    //    }
-    if (!property)
+    if (!property) // ### error
         return true;
 
     if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
@@ -1674,3 +1669,78 @@ bool QQmlComponentAndAliasResolver::resolveAliases()
     }
     return true;
 }
+
+
+QQmlPropertyValidator::QQmlPropertyValidator(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit,
+                                             const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes,
+                                             const QList<QQmlPropertyCache *> &propertyCaches, const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent)
+    : QQmlCompilePass(url, qmlUnit)
+    , resolvedTypes(resolvedTypes)
+    , propertyCaches(propertyCaches)
+    , objectIndexToIdPerComponent(objectIndexToIdPerComponent)
+{
+}
+
+bool QQmlPropertyValidator::validate()
+{
+    for (int i = 0; i < qmlUnit->nObjects; ++i) {
+        const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i);
+        if (stringAt(obj->inheritedTypeNameIndex).isEmpty())
+            continue;
+
+        if (isComponent(i))
+            continue;
+
+        QQmlPropertyCache *propertyCache = propertyCaches.value(i);
+        Q_ASSERT(propertyCache);
+
+        if (!validateObject(obj, i, propertyCache))
+            return false;
+    }
+    return true;
+}
+
+bool QQmlPropertyValidator::validateObject(const QV4::CompiledData::Object *obj, int objectIndex, QQmlPropertyCache *propertyCache)
+{
+    PropertyResolver propertyResolver(propertyCache);
+
+    QQmlPropertyData *defaultProperty = propertyCache->defaultProperty();
+
+    const QV4::CompiledData::Binding *binding = obj->bindingTable();
+    for (int i = 0; i < obj->nBindings; ++i, ++binding) {
+        if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty
+            || binding->type == QV4::CompiledData::Binding::Type_GroupProperty)
+            continue;
+
+        const QString name = stringAt(binding->propertyNameIndex);
+
+        bool bindingToDefaultProperty = false;
+
+        bool notInRevision = false;
+        QQmlPropertyData *pd = 0;
+        if (!name.isEmpty()) {
+            pd = propertyResolver.property(name, &notInRevision);
+
+            if (notInRevision) {
+                QString typeName = stringAt(obj->inheritedTypeNameIndex);
+                QQmlCompiledData::TypeReference type = resolvedTypes.value(objectIndex);
+                if (type.type) {
+                    COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(type.type->module()).arg(type.majorVersion).arg(type.minorVersion));
+                } else {
+                    COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name));
+                }
+            }
+        } else {
+           pd = defaultProperty;
+           bindingToDefaultProperty = true;
+        }
+
+        if (!pd) {
+            if (bindingToDefaultProperty) {
+                COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent default property"));
+            } else {
+                COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent property \"%1\"").arg(name));
+            }
+        }
+    }
+}
index 8412e22..ec4b362 100644 (file)
@@ -115,6 +115,27 @@ protected:
     QHash<int, QHash<int, int> > *objectIndexToIdPerComponent;
 };
 
+class QQmlPropertyValidator : public QQmlCompilePass
+{
+    Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
+public:
+    QQmlPropertyValidator(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit,
+                          const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes,
+                          const QList<QQmlPropertyCache *> &propertyCaches,
+                          const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent);
+
+    bool validate();
+
+private:
+    bool validateObject(const QV4::CompiledData::Object *obj, int objectIndex, QQmlPropertyCache *propertyCache);
+
+    bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); }
+
+    const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes;
+    const QList<QQmlPropertyCache *> &propertyCaches;
+    const QHash<int, QHash<int, int> > objectIndexToIdPerComponent;
+};
+
 class QmlObjectCreator : public QQmlCompilePass
 {
     Q_DECLARE_TR_FUNCTIONS(QmlObjectCreator)
index 63dff01..3dd071d 100644 (file)
@@ -2297,6 +2297,8 @@ void QQmlTypeData::compile()
                 ref.type = resolvedType->type;
                 Q_ASSERT(ref.type);
             }
+            ref.majorVersion = resolvedType->majorVersion;
+            ref.minorVersion = resolvedType->minorVersion;
             m_compiledData->resolvedTypes.insert(resolvedType.key(), ref);
         }
 
@@ -2425,6 +2427,14 @@ void QQmlTypeData::compile()
             }
         }
 
+        // Sanity check property bindings
+        if (errors.isEmpty()) {
+            QQmlPropertyValidator validator(m_compiledData->url, m_compiledData->qmlUnit, m_compiledData->resolvedTypes,
+                                            m_compiledData->propertyCaches, m_compiledData->objectIndexToIdPerComponent);
+            if (!validator.validate())
+                errors << validator.errors;
+        }
+
         if (!errors.isEmpty()) {
             setError(errors);
             m_compiledData->release();