forked from qt-creator/qt-creator
Semantic checking for easing curve names.
Done-with: Christian Kamm
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user