Commit 78b31454 authored by Erik Verbruggen's avatar Erik Verbruggen

Semantic checking for easing curve names.

Done-with: Christian Kamm
parent 5e449a80
......@@ -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);
}
......@@ -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;
......
......@@ -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();
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment