QmlJS.Check: Warnings for qml code not supported by Qt Quick Designer

This patch adds several warnings for qml code not supported by Qt Quick
Designer.

* WarnImperativeCodeNotEditableInVisualDesigner:
   This warns about imperative code affecting a visual property.
   e.g.: "x = 10;"

* WarnUnsupportedTypeInVisualDesigner:
    This warns about types which are currently not supported.

* WarnReferenceToParentItemNotSupportedByDesigner:
    This warns about things like: "width: parent.width" in
    the root item.

* WarnUndefinedValueForDesigner:
    This warns about visual properties that cannot be evaluated in
    the local context.
    e.g.: "x: somethingNotDefinedInTheLocalContext.x"

* WarnStatesOnlyInRootItemForDesigner:
    This warns about states not defined in the root item.

All the Qt Quick designer related warnings are disabled by default
in Check.

Change-Id: If31a8199fb95dc8bf6ac613634a2e442e436e267
Reviewed-by: Christian Kamm <kamm@incasoftware.de>
This commit is contained in:
Thomas Hartmann
2012-09-17 14:06:03 +02:00
parent 484d4dc26e
commit e5699c0925
4 changed files with 148 additions and 7 deletions

View File

@@ -503,8 +503,44 @@ private:
bool _seenNonDeclarationStatement;
};
class VisualAspectsPropertyBlackList : public QStringList
{
public:
VisualAspectsPropertyBlackList()
{
(*this) << QLatin1String("x") << QLatin1String("y") << QLatin1String("z")
<< QLatin1String("width") << QLatin1String("height") << QLatin1String("color")
<< QLatin1String("opacity") << QLatin1String("scale")
<< QLatin1String("rotation") << QLatin1String("margins")
<< QLatin1String("verticalCenterOffset") << QLatin1String("horizontalCenterOffset")
<< QLatin1String("baselineOffset") << QLatin1String("bottomMargin")
<< QLatin1String("topMargin") << QLatin1String("leftMargin")
<< QLatin1String("rightMargin") << QLatin1String("baseline")
<< QLatin1String("centerIn") << QLatin1String("fill")
<< QLatin1String("left") << QLatin1String("right")
<< QLatin1String("mirrored") << QLatin1String("verticalCenter")
<< QLatin1String("horizontalCenter");
}
};
class UnsupportedTypesByVisualDesigner : public QStringList
{
public:
UnsupportedTypesByVisualDesigner()
{
(*this) << QLatin1String("Transform") << QLatin1String("Timer")
<< QLatin1String("Rotation") << QLatin1String("Scale")
<< QLatin1String("Translate") << QLatin1String("Package")
<< QLatin1String("Particles");
}
};
} // end of anonymous namespace
Q_GLOBAL_STATIC(VisualAspectsPropertyBlackList, visualAspectsPropertyBlackList)
Q_GLOBAL_STATIC(UnsupportedTypesByVisualDesigner, unsupportedTypesByVisualDesigner)
Check::Check(Document::Ptr doc, const ContextPtr &context)
: _doc(doc)
, _context(context)
@@ -524,6 +560,11 @@ Check::Check(Document::Ptr doc, const ContextPtr &context)
disableMessage(HintBinaryOperatorSpacing);
disableMessage(HintOneStatementPerLine);
disableMessage(HintExtraParentheses);
disableMessage(WarnImperativeCodeNotEditableInVisualDesigner);
disableMessage(WarnUnsupportedTypeInVisualDesigner);
disableMessage(WarnReferenceToParentItemNotSupportedByVisualDesigner);
disableMessage(WarnUndefinedValueForVisualDesigner);
disableMessage(WarnStatesOnlyInRootItemForVisualDesigner);
}
Check::~Check()
@@ -569,21 +610,28 @@ bool Check::visit(UiProgram *)
bool Check::visit(UiObjectInitializer *)
{
QString typeName;
m_propertyStack.push(StringSet());
UiObjectDefinition *objectDefinition = cast<UiObjectDefinition *>(parent());
if (objectDefinition && objectDefinition->qualifiedTypeNameId->name == "Component")
m_idStack.push(StringSet());
UiObjectBinding *objectBinding = cast<UiObjectBinding *>(parent());
if (objectBinding && objectBinding->qualifiedTypeNameId->name == "Component")
m_idStack.push(StringSet());
UiQualifiedId *qualifiedTypeId = qualifiedTypeNameId(parent());
if (qualifiedTypeId) {
typeName = qualifiedTypeId->name.toString();
if (typeName == QLatin1String("Component"))
m_idStack.push(StringSet());
}
if (!typeName.isEmpty() && typeName.at(0).isUpper())
m_typeStack.push(typeName);
if (m_idStack.isEmpty())
m_idStack.push(StringSet());
return true;
}
void Check::endVisit(UiObjectInitializer *)
{
m_propertyStack.pop();
m_typeStack.pop();
UiObjectDefinition *objectDenition = cast<UiObjectDefinition *>(parent());
if (objectDenition && objectDenition->qualifiedTypeNameId->name == "Component")
m_idStack.pop();
@@ -619,6 +667,57 @@ bool Check::visit(UiObjectBinding *ast)
return false;
}
static bool expressionAffectsVisualAspects(BinaryExpression *expression)
{
if (expression->op == QSOperator::Assign
|| expression->op == QSOperator::InplaceSub
|| expression->op == QSOperator::InplaceAdd
|| expression->op == QSOperator::InplaceDiv
|| expression->op == QSOperator::InplaceMul
|| expression->op == QSOperator::InplaceOr
|| expression->op == QSOperator::InplaceXor
|| expression->op == QSOperator::InplaceAnd) {
const ExpressionNode *lhsValue = expression->left;
if (const IdentifierExpression* identifierExpression = cast<const IdentifierExpression *>(lhsValue)) {
if (visualAspectsPropertyBlackList()->contains(identifierExpression->name.toString()))
return true;
} else if (const FieldMemberExpression* fieldMemberExpression = cast<const FieldMemberExpression *>(lhsValue)) {
if (visualAspectsPropertyBlackList()->contains(fieldMemberExpression->name.toString()))
return true;
}
}
return false;
}
static UiQualifiedId *getRightMostIdentifier(UiQualifiedId *typeId)
{
if (typeId->next)
return getRightMostIdentifier(typeId->next);
return typeId;
}
static bool checkTypeForDesignerSupport(UiQualifiedId *typeId)
{
return unsupportedTypesByVisualDesigner()->contains(getRightMostIdentifier(typeId)->name.toString());
}
static bool checkTopLevelBindingForParentReference(ExpressionStatement *expStmt, const QString &source)
{
if (!expStmt)
return false;
SourceLocation location = locationFromRange(expStmt->firstSourceLocation(), expStmt->lastSourceLocation());
QString stmtSource = source.mid(location.begin(), location.length);
if (stmtSource.contains(QRegExp("(^|\\W)parent\\.")))
return true;
return false;
}
void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
UiObjectInitializer *initializer)
{
@@ -630,9 +729,16 @@ void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
return;
}
const SourceLocation typeErrorLocation = fullLocationForQualifiedId(typeId);
if (checkTypeForDesignerSupport(typeId))
addMessage(WarnUnsupportedTypeInVisualDesigner, typeErrorLocation);
if (m_typeStack.count() > 1 && getRightMostIdentifier(typeId)->name.toString() == QLatin1String("State"))
addMessage(WarnStatesOnlyInRootItemForVisualDesigner, typeErrorLocation);
bool typeError = false;
if (_importsOk) {
const SourceLocation typeErrorLocation = fullLocationForQualifiedId(typeId);
const ObjectValue *prototype = _context->lookupType(_doc.data(), typeId);
if (!prototype) {
typeError = true;
@@ -712,6 +818,13 @@ bool Check::visit(UiScriptBinding *ast)
m_idStack.top().insert(id);
}
if (m_typeStack.count() == 1
&& visualAspectsPropertyBlackList()->contains(ast->qualifiedId->name.toString())
&& checkTopLevelBindingForParentReference(cast<ExpressionStatement *>(ast->statement), _doc->source())) {
addMessage(WarnReferenceToParentItemNotSupportedByVisualDesigner,
locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation()));
}
checkProperty(ast->qualifiedId);
if (!ast->statement)
@@ -722,6 +835,12 @@ bool Check::visit(UiScriptBinding *ast)
Evaluate evaluator(&_scopeChain);
const Value *rhsValue = evaluator(ast->statement);
if (visualAspectsPropertyBlackList()->contains(ast->qualifiedId->name.toString()) &&
rhsValue->asUndefinedValue()) {
addMessage(WarnUndefinedValueForVisualDesigner,
locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation()));
}
const SourceLocation loc = locationFromRange(ast->statement->firstSourceLocation(),
ast->statement->lastSourceLocation());
AssignmentCheck assignmentCheck;
@@ -911,6 +1030,11 @@ bool Check::visit(BinaryExpression *ast)
addMessage(HintBinaryOperatorSpacing, op);
}
SourceLocation expressionSourceLocation = locationFromRange(ast->firstSourceLocation(),
ast->lastSourceLocation());
if (expressionAffectsVisualAspects(ast))
addMessage(WarnImperativeCodeNotEditableInVisualDesigner, expressionSourceLocation);
// check ==, !=
if (ast->op == QSOperator::Equal || ast->op == QSOperator::NotEqual) {
Evaluate eval(&_scopeChain);

View File

@@ -133,6 +133,7 @@ private:
QList<AST::Node *> _chain;
QStack<StringSet> m_idStack;
QStack<StringSet> m_propertyStack;
QStack<QString> m_typeStack;
class MessageTypeAndSuppression
{

View File

@@ -215,6 +215,17 @@ StaticAnalysisMessages::StaticAnalysisMessages()
tr("maximum string value length is %1"), 1);
newMsg(ErrInvalidArrayValueLength, Error,
tr("%1 elements expected in array value"), 1);
newMsg(WarnImperativeCodeNotEditableInVisualDesigner, Warning,
tr("Imperative code is not supported in the Qt Quick Designer"));
newMsg(WarnUnsupportedTypeInVisualDesigner, Warning,
tr("This type is not supported in the Qt Quick Designer"));
newMsg(WarnReferenceToParentItemNotSupportedByVisualDesigner, Warning,
tr("Reference to parent item cannot be resolved correctly by the Qt Quick Designer"));
newMsg(WarnUndefinedValueForVisualDesigner, Warning,
tr("This visual property binding cannot be evaluted in the local context "
"and might not show up in Qt Quick Designer as expected"));
newMsg(WarnStatesOnlyInRootItemForVisualDesigner, Warning,
tr("Qt Quick Designer only supports states in the root item "));
}
} // anonymous namespace

View File

@@ -101,6 +101,11 @@ enum Type
WarnConfusingExpressionStatement = 127,
HintDeclarationsShouldBeAtStartOfFunction = 201,
HintOneStatementPerLine = 202,
WarnImperativeCodeNotEditableInVisualDesigner = 203,
WarnUnsupportedTypeInVisualDesigner = 204,
WarnReferenceToParentItemNotSupportedByVisualDesigner = 205,
WarnUndefinedValueForVisualDesigner = 206,
WarnStatesOnlyInRootItemForVisualDesigner = 207,
ErrUnknownComponent = 300,
ErrCouldNotResolvePrototypeOf = 301,
ErrCouldNotResolvePrototype = 302,