diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index c757752d7f6154dda3387c2b5fe18e81c2292358..7a794cb03baa7e56a61e577e3793fb1b0c86846c 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -42,6 +42,9 @@ static const char *invalid_property_name = QT_TRANSLATE_NOOP("QmlJS::Check", "' static const char *unknown_type = QT_TRANSLATE_NOOP("QmlJS::Check", "unknown type"); static const char *has_no_members = QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' does not have members"); static const char *is_not_a_member = QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' is not a member of '%2'"); +static const char *easing_curve_not_a_string = QT_TRANSLATE_NOOP("QmlJS::Check", "easing-curve name is not a string"); +static const char *unknown_easing_curve_name = QT_TRANSLATE_NOOP("QmlJS::Check", "unknown easing-curve name"); +static const char *value_might_be_undefined = QT_TRANSLATE_NOOP("QmlJS::Check", "value might be 'undefined'"); } // namespace Messages static inline QString tr(const char *msg) @@ -176,11 +179,50 @@ void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId, bool Check::visit(UiScriptBinding *ast) { - checkScopeObjectMember(ast->qualifiedId); + const Value *lhsValue = checkScopeObjectMember(ast->qualifiedId); + if (lhsValue) { + // ### Fix the evaluator to accept statements! + if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement)) { + Evaluate evaluator(&_context); + const Value *rhsValue = evaluator(expStmt->expression); + + const SourceLocation loc = locationFromRange(expStmt->firstSourceLocation(), expStmt->lastSourceLocation()); + checkPropertyAssignment(loc, lhsValue, rhsValue, expStmt->expression); + } + + } return true; } +void Check::checkPropertyAssignment(const SourceLocation &location, + const Interpreter::Value *lhsValue, + const Interpreter::Value *rhsValue, + ExpressionNode *ast) +{ + 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; + } + + if (StringLiteral *string = cast<StringLiteral *>(ast)) { + const QString value = string->value->asString(); + // ### 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 = value.left(value.indexOf(QLatin1Char('('))); + if (!EasingCurveNameValue::curveNames().contains(curveName)) { + error(location, tr(Messages::unknown_easing_curve_name)); + } + } + } +} + bool Check::visit(UiArrayBinding *ast) { checkScopeObjectMember(ast->qualifiedId); @@ -188,20 +230,20 @@ bool Check::visit(UiArrayBinding *ast) return true; } -void Check::checkScopeObjectMember(const UiQualifiedId *id) +const Value *Check::checkScopeObjectMember(const UiQualifiedId *id) { if (_allowAnyProperty) - return; + return 0; const ObjectValue *scopeObject = _context.qmlScopeObject(); if (! id) - return; // ### error? + return 0; // ### error? QString propertyName = id->name->asString(); if (propertyName == QLatin1String("id") && ! id->next) - return; + return 0; // ### should probably be a special value // attached properties bool isAttachedProperty = false; @@ -211,7 +253,7 @@ void Check::checkScopeObjectMember(const UiQualifiedId *id) } if (! scopeObject) - return; + return 0; // global lookup for first part of id const Value *value = scopeObject->lookupMember(propertyName, &_context); @@ -224,7 +266,7 @@ void Check::checkScopeObjectMember(const UiQualifiedId *id) // can't look up members for attached properties if (isAttachedProperty) - return; + return 0; // member lookup const UiQualifiedId *idPart = id; @@ -233,7 +275,7 @@ void Check::checkScopeObjectMember(const UiQualifiedId *id) if (! objectValue) { error(idPart->identifierToken, tr(Messages::has_no_members).arg(propertyName)); - return; + return 0; } idPart = idPart->next; @@ -244,9 +286,11 @@ void Check::checkScopeObjectMember(const UiQualifiedId *id) error(idPart->identifierToken, tr(Messages::is_not_a_member).arg(propertyName, objectValue->className())); - return; + return 0; } } + + return value; } void Check::error(const AST::SourceLocation &loc, const QString &message) @@ -258,3 +302,12 @@ void Check::warning(const AST::SourceLocation &loc, const QString &message) { _messages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message)); } + +SourceLocation Check::locationFromRange(const SourceLocation &start, + const SourceLocation &end) +{ + return SourceLocation(start.offset, + end.end() - start.begin(), + start.startLine, + start.startColumn); +} diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h index dea80974179f5acad8cf491baaae14af873b1e18..56ec5c0c83cae93e2abb6d0a83456f08e6355768 100644 --- a/src/libs/qmljs/qmljscheck.h +++ b/src/libs/qmljs/qmljscheck.h @@ -55,10 +55,16 @@ protected: private: void visitQmlObject(AST::Node *ast, AST::UiQualifiedId *typeId, AST::UiObjectInitializer *initializer); - void checkScopeObjectMember(const AST::UiQualifiedId *id); + 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 warning(const AST::SourceLocation &loc, const QString &message); void error(const AST::SourceLocation &loc, const QString &message); + static AST::SourceLocation locationFromRange(const AST::SourceLocation &start, + const AST::SourceLocation &end); Document::Ptr _doc; Snapshot _snapshot; diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 7caee728108c365bfa7d580ab88b3f8e71563ae7..20b1f47dbe7545e28318a317c7e0fd5816eb22a7 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -41,6 +41,7 @@ # include <QtDeclarative/private/qmlgraphicsanchors_p.h> // ### remove me # include <QtDeclarative/private/qmlgraphicsrectangle_p.h> // ### remove me # include <QtDeclarative/private/qmlvaluetype_p.h> // ### remove me +# include <QtDeclarative/private/qmlanimation_p.h> // ### remove me #endif using namespace QmlJS::Interpreter; @@ -329,8 +330,29 @@ const Value *QmlObjectValue::propertyValue(const QMetaProperty &prop) const break; } // end of switch + const QString typeName = prop.typeName(); + if (typeName == QLatin1String("QmlGraphicsAnchorLine")) { + ObjectValue *object = engine()->newObject(/*prototype =*/ 0); + object->setClassName(QLatin1String("AnchorLine")); + value = object; + } + if (value->asStringValue() && prop.name() == QLatin1String("easing") + && isDerivedFrom(&QmlPropertyAnimation::staticMetaObject)) { + value = engine()->easingCurveNameValue(); + } + return value; } + +bool QmlObjectValue::isDerivedFrom(const QMetaObject *base) const +{ + for (const QMetaObject *iter = _metaObject; iter; iter = iter->superClass()) { + if (iter == base) + return true; + } + return false; +} + #endif namespace { @@ -584,6 +606,10 @@ void ValueVisitor::visit(const Reference *) { } +void ValueVisitor::visit(const EasingCurveNameValue *) +{ +} + //////////////////////////////////////////////////////////////////////////////// // Value //////////////////////////////////////////////////////////////////////////////// @@ -640,6 +666,11 @@ const Reference *Value::asReference() const return 0; } +const EasingCurveNameValue *Value::asEasingCurveNameValue() const +{ + return 0; +} + //////////////////////////////////////////////////////////////////////////////// // Values //////////////////////////////////////////////////////////////////////////////// @@ -860,6 +891,67 @@ const Value *Reference::value(Context *) const return _engine->undefinedValue(); } +void EasingCurveNameValue::accept(ValueVisitor *visitor) const +{ + visitor->visit(this); +} + +QSet<QString> EasingCurveNameValue::_curveNames; +QSet<QString> EasingCurveNameValue::curveNames() +{ + if (_curveNames.isEmpty()) { + _curveNames = QSet<QString>() + << "easeLinear" + << "easeInQuad" + << "easeOutQuad" + << "easeInOutQuad" + << "easeOutInQuad" + << "easeInCubic" + << "easeOutCubic" + << "easeInOutCubic" + << "easeOutInCubic" + << "easeInQuart" + << "easeOutQuart" + << "easeInOutQuart" + << "easeOutInQuart" + << "easeInQuint" + << "easeOutQuint" + << "easeInOutQuint" + << "easeOutInQuint" + << "easeInSine" + << "easeOutSine" + << "easeInOutSine" + << "easeOutInSine" + << "easeInExpo" + << "easeOutExpo" + << "easeInOutExpo" + << "easeOutInExpo" + << "easeInCirc" + << "easeOutCirc" + << "easeInOutCirc" + << "easeOutInCirc" + << "easeInElastic" + << "easeOutElastic" + << "easeInOutElastic" + << "easeOutInElastic" + << "easeInBack" + << "easeOutBack" + << "easeInOutBack" + << "easeOutInBack" + << "easeInBounce" + << "easeOutBounce" + << "easeInOutBounce" + << "easeOutInBounce"; + } + + return _curveNames; +} + +const EasingCurveNameValue *EasingCurveNameValue::asEasingCurveNameValue() const +{ + return this; +} + MemberProcessor::MemberProcessor() { } @@ -1499,6 +1591,11 @@ const StringValue *Engine::stringValue() const return &_stringValue; } +const EasingCurveNameValue *Engine::easingCurveNameValue() const +{ + return &_easingCurveNameValue; +} + const Value *Engine::newArray() { return arrayCtor()->construct(); diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 8f0fa51532f06290c42a25f1a0f97f73a53b27c7..a1fac700ec85f2dcdc740948d51709b00687f88e 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -60,6 +60,7 @@ class StringValue; class ObjectValue; class FunctionValue; class Reference; +class EasingCurveNameValue; typedef QList<const Value *> ValueList; @@ -80,6 +81,7 @@ public: virtual void visit(const ObjectValue *); virtual void visit(const FunctionValue *); virtual void visit(const Reference *); + virtual void visit(const EasingCurveNameValue *); }; //////////////////////////////////////////////////////////////////////////////// @@ -102,6 +104,7 @@ public: virtual const ObjectValue *asObjectValue() const; virtual const FunctionValue *asFunctionValue() const; virtual const Reference *asReference() const; + virtual const EasingCurveNameValue *asEasingCurveNameValue() const; virtual void accept(ValueVisitor *) const = 0; @@ -158,6 +161,12 @@ template <> Q_INLINE_TEMPLATE const Reference *value_cast(const Value *v) else return 0; } +template <> Q_INLINE_TEMPLATE const EasingCurveNameValue *value_cast(const Value *v) +{ + if (v) return v->asEasingCurveNameValue(); + else return 0; +} + //////////////////////////////////////////////////////////////////////////////// // Value nodes //////////////////////////////////////////////////////////////////////////////// @@ -280,6 +289,18 @@ private: Engine *_engine; }; +class QMLJS_EXPORT EasingCurveNameValue: public Value +{ + static QSet<QString> _curveNames; + +public: + static QSet<QString> curveNames(); + + // Value interface + virtual const EasingCurveNameValue *asEasingCurveNameValue() const; + virtual void accept(ValueVisitor *) const; +}; + class QMLJS_EXPORT ObjectValue: public Value { public: @@ -339,6 +360,7 @@ public: protected: const Value *findOrCreateSignature(int index, const QMetaMethod &method, QString *methodName) const; + bool isDerivedFrom(const QMetaObject *base) const; private: const QMetaObject *_metaObject; @@ -537,6 +559,7 @@ public: const NumberValue *numberValue() const; const BooleanValue *booleanValue() const; const StringValue *stringValue() const; + const EasingCurveNameValue *easingCurveNameValue() const; ObjectValue *newObject(const ObjectValue *prototype); ObjectValue *newObject(); @@ -625,6 +648,7 @@ private: NumberValue _numberValue; BooleanValue _booleanValue; StringValue _stringValue; + EasingCurveNameValue _easingCurveNameValue; QList<Value *> _registeredValues; ConvertToNumber _convertToNumber;