From f0674aa7e9853cf5d3d48e3832a67de1b4033ad9 Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Tue, 23 Feb 2010 17:02:50 +0100
Subject: [PATCH] Add check for anchor line, changed value assignment checks
 into visitor.

Done-with: Erik Verbruggen
---
 src/libs/qmljs/qmljscheck.cpp       | 174 +++++++++++++++++-----------
 src/libs/qmljs/qmljscheck.h         |   5 -
 src/libs/qmljs/qmljsinterpreter.cpp |  43 ++++++-
 src/libs/qmljs/qmljsinterpreter.h   |  22 ++++
 4 files changed, 168 insertions(+), 76 deletions(-)

diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index 8269f92c893..8a7592dfc0e 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -62,6 +62,108 @@ using namespace QmlJS;
 using namespace QmlJS::AST;
 using namespace QmlJS::Interpreter;
 
+
+namespace {
+
+class AssignmentCheck : public ValueVisitor
+{
+public:
+    DiagnosticMessage operator()(
+            const SourceLocation &location,
+            const Interpreter::Value *lhsValue,
+            const Interpreter::Value *rhsValue,
+            ExpressionNode *ast)
+    {
+        _message = DiagnosticMessage(DiagnosticMessage::Error, location, QString());
+        _rhsValue = rhsValue;
+        _ast = ast;
+
+        if (lhsValue)
+            lhsValue->accept(this);
+
+        return _message;
+    }
+
+    virtual void visit(const NumberValue *)
+    {
+        // ### Consider enums: elide: "ElideLeft" is valid, but currently elide is a NumberValue.
+        if (/*cast<StringLiteral *>(_ast)
+                ||*/ _ast->kind == Node::Kind_TrueLiteral
+                || _ast->kind == Node::Kind_FalseLiteral) {
+            _message.message = QCoreApplication::translate("QmlJS::Check", "numerical value expected");
+        }
+    }
+
+    virtual void visit(const BooleanValue *)
+    {
+        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);
+
+        if (cast<StringLiteral *>(_ast)
+                || cast<NumericLiteral *>(_ast)
+                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))) {
+            _message.message = QCoreApplication::translate("QmlJS::Check", "boolean value expected");
+        }
+    }
+
+    virtual void visit(const StringValue *)
+    {
+        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);
+
+        if (cast<NumericLiteral *>(_ast)
+                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))
+                || _ast->kind == Node::Kind_TrueLiteral
+                || _ast->kind == Node::Kind_FalseLiteral) {
+            _message.message = QCoreApplication::translate("QmlJS::Check", "string value expected");
+        }
+    }
+
+    virtual void visit(const EasingCurveNameValue *)
+    {
+        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
+            const QString curveName = stringLiteral->value->asString();
+
+            // ### update when easing changes hit master
+            if (!EasingCurveNameValue::curveNames().contains(curveName)) {
+                _message.message = tr(Messages::unknown_easing_curve_name);
+            }
+        } else if (_rhsValue->asUndefinedValue()) {
+            _message.kind = DiagnosticMessage::Warning;
+            _message.message = tr(Messages::value_might_be_undefined);
+        } else if (! _rhsValue->asStringValue()) {
+            _message.message = tr(Messages::easing_curve_not_a_string);
+        }
+    }
+
+    virtual void visit(const ColorValue *)
+    {
+        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
+            const QString colorString = stringLiteral->value->asString();
+
+#ifndef NO_DECLARATIVE_BACKEND
+            bool ok = false;
+            QmlStringConverters::colorFromString(colorString, &ok);
+            if (!ok)
+                _message.message = QCoreApplication::translate("QmlJS::Check", "not a valid color");
+#endif
+        } else {
+            visit((StringValue *)0);
+        }
+    }
+
+    virtual void visit(const AnchorLineValue *)
+    {
+        if (! (_rhsValue->asAnchorLineValue() || _rhsValue->asUndefinedValue()))
+            _message.message = QCoreApplication::translate("QmlJS::Check", "expected anchor line");
+    }
+
+    DiagnosticMessage _message;
+    const Value *_rhsValue;
+    ExpressionNode *_ast;
+};
+
+} // end of anonymous namespace
+
+
 Check::Check(Document::Ptr doc, const Snapshot &snapshot)
     : _doc(doc)
     , _snapshot(snapshot)
@@ -131,17 +233,6 @@ void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
     _scopeBuilder.pop();
 }
 
-void Check::errorOnWrongRhs(const SourceLocation &loc, const Value *lhsValue)
-{
-    if (lhsValue->asBooleanValue()) {
-        error(loc, QCoreApplication::translate("QmlJS::Check", "boolean value expected"));
-    } else if (lhsValue->asNumberValue()) {
-        error(loc, QCoreApplication::translate("QmlJS::Check", "numerical value expected"));
-    } else if (lhsValue->asStringValue()) {
-        error(loc, QCoreApplication::translate("QmlJS::Check", "string value expected"));
-    }
-}
-
 bool Check::visit(UiScriptBinding *ast)
 {
     // special case for id property
@@ -181,7 +272,10 @@ bool Check::visit(UiScriptBinding *ast)
 
             const SourceLocation loc = locationFromRange(expStmt->firstSourceLocation(),
                                                          expStmt->lastSourceLocation());
-            checkPropertyAssignment(loc, lhsValue, rhsValue, expr);
+            AssignmentCheck assignmentCheck;
+            DiagnosticMessage message = assignmentCheck(loc, lhsValue, rhsValue, expr);
+            if (! message.message.isEmpty())
+                _messages += message;
         }
 
     }
@@ -189,62 +283,6 @@ bool Check::visit(UiScriptBinding *ast)
     return true;
 }
 
-void Check::checkPropertyAssignment(const SourceLocation &location,
-                                    const Interpreter::Value *lhsValue,
-                                    const Interpreter::Value *rhsValue,
-                                    ExpressionNode *ast)
-{
-    UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(ast);
-
-    // Qml is particularly strict with literals
-    if (StringLiteral *stringLiteral = cast<StringLiteral *>(ast)) {
-        const QString string = stringLiteral->value->asString();
-
-        if (lhsValue->asStringValue()) {
-            // okay
-        } else if (lhsValue->asColorValue()) {
-#ifndef NO_DECLARATIVE_BACKEND
-            bool ok = false;
-            QmlStringConverters::colorFromString(string, &ok);
-            if (!ok)
-                error(location, QCoreApplication::translate("QmlJS::Check", "not a valid color"));
-#endif
-        } else if (lhsValue->asEasingCurveNameValue()) {
-            // ### do something with easing-curve attributes.
-            // ### Incomplete documentation at: http://qt.nokia.com/doc/4.7-snapshot/qml-propertyanimation.html#easing-prop
-            // ### The implementation is at: src/declarative/util/qmlanimation.cpp
-            const QString curveName = string.left(string.indexOf(QLatin1Char('(')));
-            if (!EasingCurveNameValue::curveNames().contains(curveName)) {
-                error(location, tr(Messages::unknown_easing_curve_name));
-            }
-        } else {
-            errorOnWrongRhs(location, lhsValue);
-        }
-    } else if ((ast->kind == Node::Kind_TrueLiteral
-                || ast->kind == Node::Kind_FalseLiteral)
-               && ! lhsValue->asBooleanValue()) {
-        errorOnWrongRhs(location, lhsValue);
-    } else if (cast<NumericLiteral *>(ast)
-               && ! lhsValue->asNumberValue()) {
-        errorOnWrongRhs(location, lhsValue);
-    } else if (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression)
-               && ! lhsValue->asNumberValue()) {
-        errorOnWrongRhs(location, lhsValue);
-    } else {
-        // rhs is not a literal
-        if (lhsValue->asEasingCurveNameValue()) {
-            const StringValue *rhsStringValue = rhsValue->asStringValue();
-            if (!rhsStringValue) {
-                if (rhsValue->asUndefinedValue())
-                    warning(location, tr(Messages::value_might_be_undefined));
-                else
-                    error(location, tr(Messages::easing_curve_not_a_string));
-                return;
-            }
-        }
-    }
-}
-
 bool Check::visit(UiArrayBinding *ast)
 {
     checkScopeObjectMember(ast->qualifiedId);
diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h
index a50953e15fa..d49057841c8 100644
--- a/src/libs/qmljs/qmljscheck.h
+++ b/src/libs/qmljs/qmljscheck.h
@@ -57,11 +57,6 @@ private:
     void visitQmlObject(AST::Node *ast, AST::UiQualifiedId *typeId,
                         AST::UiObjectInitializer *initializer);
     const Interpreter::Value *checkScopeObjectMember(const AST::UiQualifiedId *id);
-    void checkPropertyAssignment(const AST::SourceLocation &location,
-                                 const Interpreter::Value *lhsValue,
-                                 const Interpreter::Value *rhsValue,
-                                 QmlJS::AST::ExpressionNode *ast);
-    void errorOnWrongRhs(const AST::SourceLocation &loc, const Interpreter::Value *lhsValue);
 
     void warning(const AST::SourceLocation &loc, const QString &message);
     void error(const AST::SourceLocation &loc, const QString &message);
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index 9d4461749f3..9975afdacb6 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -336,9 +336,7 @@ const Value *QmlObjectValue::propertyValue(const QMetaProperty &prop) const
 
     const QString typeName = prop.typeName();
     if (typeName == QLatin1String("QmlGraphicsAnchorLine")) {
-        ObjectValue *object = engine()->newObject(/*prototype =*/ 0);
-        object->setClassName(QLatin1String("AnchorLine"));
-        value = object;
+        value = engine()->anchorLineValue();
     }
     if (value->asStringValue() && prop.name() == QLatin1String("easing")
             && isDerivedFrom(&QmlPropertyAnimation::staticMetaObject)) {
@@ -618,6 +616,10 @@ void ValueVisitor::visit(const ColorValue *)
 {
 }
 
+void ValueVisitor::visit(const AnchorLineValue *)
+{
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Value
 ////////////////////////////////////////////////////////////////////////////////
@@ -684,6 +686,11 @@ const ColorValue *Value::asColorValue() const
     return 0;
 }
 
+const AnchorLineValue *Value::asAnchorLineValue() const
+{
+    return 0;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Values
 ////////////////////////////////////////////////////////////////////////////////
@@ -1000,6 +1007,16 @@ const ColorValue *ColorValue::asColorValue() const
     return this;
 }
 
+void AnchorLineValue::accept(ValueVisitor *visitor) const
+{
+    visitor->visit(this);
+}
+
+const AnchorLineValue *AnchorLineValue::asAnchorLineValue() const
+{
+    return this;
+}
+
 MemberProcessor::MemberProcessor()
 {
 }
@@ -1577,6 +1594,21 @@ void TypeId::visit(const FunctionValue *object)
         _result = QLatin1String("Function");
 }
 
+void TypeId::visit(const EasingCurveNameValue *)
+{
+    _result = QLatin1String("string");
+}
+
+void TypeId::visit(const ColorValue *)
+{
+    _result = QLatin1String("string");
+}
+
+void TypeId::visit(const AnchorLineValue *)
+{
+    _result = QLatin1String("AnchorLine");
+}
+
 Engine::Engine()
     : _objectPrototype(0),
       _functionPrototype(0),
@@ -1649,6 +1681,11 @@ const ColorValue *Engine::colorValue() const
     return &_colorValue;
 }
 
+const AnchorLineValue *Engine::anchorLineValue() const
+{
+    return &_anchorLineValue;
+}
+
 const Value *Engine::newArray()
 {
     return arrayCtor()->construct();
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 35137579e7a..164e6094026 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -62,6 +62,7 @@ class FunctionValue;
 class Reference;
 class EasingCurveNameValue;
 class ColorValue;
+class AnchorLineValue;
 
 typedef QList<const Value *> ValueList;
 
@@ -84,6 +85,7 @@ public:
     virtual void visit(const Reference *);
     virtual void visit(const EasingCurveNameValue *);
     virtual void visit(const ColorValue *);
+    virtual void visit(const AnchorLineValue *);
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -108,6 +110,7 @@ public:
     virtual const Reference *asReference() const;
     virtual const EasingCurveNameValue *asEasingCurveNameValue() const;
     virtual const ColorValue *asColorValue() const;
+    virtual const AnchorLineValue *asAnchorLineValue() const;
 
     virtual void accept(ValueVisitor *) const = 0;
 
@@ -176,6 +179,12 @@ template <> Q_INLINE_TEMPLATE const ColorValue *value_cast(const Value *v)
     else   return 0;
 }
 
+template <> Q_INLINE_TEMPLATE const AnchorLineValue *value_cast(const Value *v)
+{
+    if (v) return v->asAnchorLineValue();
+    else   return 0;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Value nodes
 ////////////////////////////////////////////////////////////////////////////////
@@ -343,6 +352,14 @@ public:
     virtual void accept(ValueVisitor *) const;
 };
 
+class QMLJS_EXPORT AnchorLineValue: public Value
+{
+public:
+    // Value interface
+    virtual const AnchorLineValue *asAnchorLineValue() const;
+    virtual void accept(ValueVisitor *) const;
+};
+
 class QMLJS_EXPORT ObjectValue: public Value
 {
 public:
@@ -585,6 +602,9 @@ protected:
     virtual void visit(const StringValue *);
     virtual void visit(const ObjectValue *object);
     virtual void visit(const FunctionValue *object);
+    virtual void visit(const EasingCurveNameValue *);
+    virtual void visit(const ColorValue *);
+    virtual void visit(const AnchorLineValue *);
 };
 
 class QMLJS_EXPORT Engine
@@ -603,6 +623,7 @@ public:
     const StringValue *stringValue() const;
     const EasingCurveNameValue *easingCurveNameValue() const;
     const ColorValue *colorValue() const;
+    const AnchorLineValue *anchorLineValue() const;
 
     ObjectValue *newObject(const ObjectValue *prototype);
     ObjectValue *newObject();
@@ -693,6 +714,7 @@ private:
     StringValue _stringValue;
     EasingCurveNameValue _easingCurveNameValue;
     ColorValue _colorValue;
+    AnchorLineValue _anchorLineValue;
     QList<Value *> _registeredValues;
 
     ConvertToNumber _convertToNumber;
-- 
GitLab