forked from qt-creator/qt-creator
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:
@@ -503,8 +503,44 @@ private:
|
|||||||
bool _seenNonDeclarationStatement;
|
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
|
} // end of anonymous namespace
|
||||||
|
|
||||||
|
Q_GLOBAL_STATIC(VisualAspectsPropertyBlackList, visualAspectsPropertyBlackList)
|
||||||
|
Q_GLOBAL_STATIC(UnsupportedTypesByVisualDesigner, unsupportedTypesByVisualDesigner)
|
||||||
|
|
||||||
Check::Check(Document::Ptr doc, const ContextPtr &context)
|
Check::Check(Document::Ptr doc, const ContextPtr &context)
|
||||||
: _doc(doc)
|
: _doc(doc)
|
||||||
, _context(context)
|
, _context(context)
|
||||||
@@ -524,6 +560,11 @@ Check::Check(Document::Ptr doc, const ContextPtr &context)
|
|||||||
disableMessage(HintBinaryOperatorSpacing);
|
disableMessage(HintBinaryOperatorSpacing);
|
||||||
disableMessage(HintOneStatementPerLine);
|
disableMessage(HintOneStatementPerLine);
|
||||||
disableMessage(HintExtraParentheses);
|
disableMessage(HintExtraParentheses);
|
||||||
|
disableMessage(WarnImperativeCodeNotEditableInVisualDesigner);
|
||||||
|
disableMessage(WarnUnsupportedTypeInVisualDesigner);
|
||||||
|
disableMessage(WarnReferenceToParentItemNotSupportedByVisualDesigner);
|
||||||
|
disableMessage(WarnUndefinedValueForVisualDesigner);
|
||||||
|
disableMessage(WarnStatesOnlyInRootItemForVisualDesigner);
|
||||||
}
|
}
|
||||||
|
|
||||||
Check::~Check()
|
Check::~Check()
|
||||||
@@ -569,21 +610,28 @@ bool Check::visit(UiProgram *)
|
|||||||
|
|
||||||
bool Check::visit(UiObjectInitializer *)
|
bool Check::visit(UiObjectInitializer *)
|
||||||
{
|
{
|
||||||
|
QString typeName;
|
||||||
m_propertyStack.push(StringSet());
|
m_propertyStack.push(StringSet());
|
||||||
UiObjectDefinition *objectDefinition = cast<UiObjectDefinition *>(parent());
|
UiQualifiedId *qualifiedTypeId = qualifiedTypeNameId(parent());
|
||||||
if (objectDefinition && objectDefinition->qualifiedTypeNameId->name == "Component")
|
if (qualifiedTypeId) {
|
||||||
m_idStack.push(StringSet());
|
typeName = qualifiedTypeId->name.toString();
|
||||||
UiObjectBinding *objectBinding = cast<UiObjectBinding *>(parent());
|
if (typeName == QLatin1String("Component"))
|
||||||
if (objectBinding && objectBinding->qualifiedTypeNameId->name == "Component")
|
m_idStack.push(StringSet());
|
||||||
m_idStack.push(StringSet());
|
}
|
||||||
|
|
||||||
|
if (!typeName.isEmpty() && typeName.at(0).isUpper())
|
||||||
|
m_typeStack.push(typeName);
|
||||||
|
|
||||||
if (m_idStack.isEmpty())
|
if (m_idStack.isEmpty())
|
||||||
m_idStack.push(StringSet());
|
m_idStack.push(StringSet());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Check::endVisit(UiObjectInitializer *)
|
void Check::endVisit(UiObjectInitializer *)
|
||||||
{
|
{
|
||||||
m_propertyStack.pop();
|
m_propertyStack.pop();
|
||||||
|
m_typeStack.pop();
|
||||||
UiObjectDefinition *objectDenition = cast<UiObjectDefinition *>(parent());
|
UiObjectDefinition *objectDenition = cast<UiObjectDefinition *>(parent());
|
||||||
if (objectDenition && objectDenition->qualifiedTypeNameId->name == "Component")
|
if (objectDenition && objectDenition->qualifiedTypeNameId->name == "Component")
|
||||||
m_idStack.pop();
|
m_idStack.pop();
|
||||||
@@ -619,6 +667,57 @@ bool Check::visit(UiObjectBinding *ast)
|
|||||||
return false;
|
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,
|
void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
|
||||||
UiObjectInitializer *initializer)
|
UiObjectInitializer *initializer)
|
||||||
{
|
{
|
||||||
@@ -630,9 +729,16 @@ void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
|
|||||||
return;
|
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;
|
bool typeError = false;
|
||||||
if (_importsOk) {
|
if (_importsOk) {
|
||||||
const SourceLocation typeErrorLocation = fullLocationForQualifiedId(typeId);
|
|
||||||
const ObjectValue *prototype = _context->lookupType(_doc.data(), typeId);
|
const ObjectValue *prototype = _context->lookupType(_doc.data(), typeId);
|
||||||
if (!prototype) {
|
if (!prototype) {
|
||||||
typeError = true;
|
typeError = true;
|
||||||
@@ -712,6 +818,13 @@ bool Check::visit(UiScriptBinding *ast)
|
|||||||
m_idStack.top().insert(id);
|
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);
|
checkProperty(ast->qualifiedId);
|
||||||
|
|
||||||
if (!ast->statement)
|
if (!ast->statement)
|
||||||
@@ -722,6 +835,12 @@ bool Check::visit(UiScriptBinding *ast)
|
|||||||
Evaluate evaluator(&_scopeChain);
|
Evaluate evaluator(&_scopeChain);
|
||||||
const Value *rhsValue = evaluator(ast->statement);
|
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(),
|
const SourceLocation loc = locationFromRange(ast->statement->firstSourceLocation(),
|
||||||
ast->statement->lastSourceLocation());
|
ast->statement->lastSourceLocation());
|
||||||
AssignmentCheck assignmentCheck;
|
AssignmentCheck assignmentCheck;
|
||||||
@@ -911,6 +1030,11 @@ bool Check::visit(BinaryExpression *ast)
|
|||||||
addMessage(HintBinaryOperatorSpacing, op);
|
addMessage(HintBinaryOperatorSpacing, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SourceLocation expressionSourceLocation = locationFromRange(ast->firstSourceLocation(),
|
||||||
|
ast->lastSourceLocation());
|
||||||
|
if (expressionAffectsVisualAspects(ast))
|
||||||
|
addMessage(WarnImperativeCodeNotEditableInVisualDesigner, expressionSourceLocation);
|
||||||
|
|
||||||
// check ==, !=
|
// check ==, !=
|
||||||
if (ast->op == QSOperator::Equal || ast->op == QSOperator::NotEqual) {
|
if (ast->op == QSOperator::Equal || ast->op == QSOperator::NotEqual) {
|
||||||
Evaluate eval(&_scopeChain);
|
Evaluate eval(&_scopeChain);
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ private:
|
|||||||
QList<AST::Node *> _chain;
|
QList<AST::Node *> _chain;
|
||||||
QStack<StringSet> m_idStack;
|
QStack<StringSet> m_idStack;
|
||||||
QStack<StringSet> m_propertyStack;
|
QStack<StringSet> m_propertyStack;
|
||||||
|
QStack<QString> m_typeStack;
|
||||||
|
|
||||||
class MessageTypeAndSuppression
|
class MessageTypeAndSuppression
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -215,6 +215,17 @@ StaticAnalysisMessages::StaticAnalysisMessages()
|
|||||||
tr("maximum string value length is %1"), 1);
|
tr("maximum string value length is %1"), 1);
|
||||||
newMsg(ErrInvalidArrayValueLength, Error,
|
newMsg(ErrInvalidArrayValueLength, Error,
|
||||||
tr("%1 elements expected in array value"), 1);
|
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
|
} // anonymous namespace
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ enum Type
|
|||||||
WarnConfusingExpressionStatement = 127,
|
WarnConfusingExpressionStatement = 127,
|
||||||
HintDeclarationsShouldBeAtStartOfFunction = 201,
|
HintDeclarationsShouldBeAtStartOfFunction = 201,
|
||||||
HintOneStatementPerLine = 202,
|
HintOneStatementPerLine = 202,
|
||||||
|
WarnImperativeCodeNotEditableInVisualDesigner = 203,
|
||||||
|
WarnUnsupportedTypeInVisualDesigner = 204,
|
||||||
|
WarnReferenceToParentItemNotSupportedByVisualDesigner = 205,
|
||||||
|
WarnUndefinedValueForVisualDesigner = 206,
|
||||||
|
WarnStatesOnlyInRootItemForVisualDesigner = 207,
|
||||||
ErrUnknownComponent = 300,
|
ErrUnknownComponent = 300,
|
||||||
ErrCouldNotResolvePrototypeOf = 301,
|
ErrCouldNotResolvePrototypeOf = 301,
|
||||||
ErrCouldNotResolvePrototype = 302,
|
ErrCouldNotResolvePrototype = 302,
|
||||||
|
|||||||
Reference in New Issue
Block a user