Add check for anchor line, changed value assignment checks into visitor.

Done-with: Erik Verbruggen
This commit is contained in:
Christian Kamm
2010-02-23 17:02:50 +01:00
parent 04d90d9563
commit f0674aa7e9
4 changed files with 168 additions and 76 deletions

View File

@@ -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);