qdoc: Report multiple QML property docss

Documentation authors sometimes make the mistake of
documenting a QML property more than once. Here, we
refer to cases where a C++ class is documented in a
.cpp file as a QML type. In this context one QML
property might be documented in two qdoc comments,
because the author of the second comment does not
search the file for an existing qdoc comment for
the property before adding the second one. When DITA
XML is generated for this case, the QML type element
will contain two <qmlproperty> elements with identical
id attributes, which is invalid XML. id attributes
must be unique within an XML document.

qdoc now reports an error for this case, indicating
that the QMLN property has been documented multiple
times.

This problem can't occur when documenting QML in a
.qml file because in .qml files, each comment must
appear directly above the thing it applies to.

Change-Id: I3a22650a58371fbda2ac7a5429fc036f41750423
Reviewed-by: Martin Smith <martin.smith@nokia.com>
This commit is contained in:
Martin Smith 2012-05-18 10:18:57 +02:00 committed by Qt by Nokia
parent 1c7421ad14
commit f3a0422acb
3 changed files with 75 additions and 20 deletions

View File

@ -960,23 +960,28 @@ Node *CppCodeParser::processTopicCommandGroup(const QString& command, const ArgL
QString module; QString module;
QString element; QString element;
QString property; QString property;
QmlClassNode* qmlClass = 0;
bool attached = (command == COMMAND_QMLATTACHEDPROPERTY); bool attached = (command == COMMAND_QMLATTACHEDPROPERTY);
ArgList::ConstIterator argsIter = args.begin(); ArgList::ConstIterator argsIter = args.begin();
arg = argsIter->first; arg = argsIter->first;
if (splitQmlPropertyArg(arg,type,module,element,property)) { if (splitQmlPropertyArg(arg,type,module,element,property)) {
QmlClassNode* qmlClass = tree_->findQmlClassNode(module,element); qmlClass = tree_->findQmlClassNode(module,element);
if (qmlClass) { if (qmlClass) {
qmlPropGroup = new QmlPropGroupNode(qmlClass,property); //,attached); qmlPropGroup = new QmlPropGroupNode(qmlClass,property); //,attached);
qmlPropGroup->setLocation(location()); qmlPropGroup->setLocation(location());
} }
} }
if (qmlPropGroup) { if (qmlPropGroup) {
const PropertyNode *correspondingProperty = 0;
if (qmlClass->hasProperty(property)) {
location().warning(tr("QML property documented multiple times: '%1'").arg(arg));
}
else {
ClassNode *correspondingClass = static_cast<QmlClassNode*>(qmlPropGroup->parent())->classNode(); ClassNode *correspondingClass = static_cast<QmlClassNode*>(qmlPropGroup->parent())->classNode();
QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached); QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached);
qmlPropNode->setLocation(location()); qmlPropNode->setLocation(location());
qmlPropNode->setQPropertyFlag(); qmlPropNode->setQPropertyFlag();
const PropertyNode *correspondingProperty = 0;
if (correspondingClass) { if (correspondingClass) {
correspondingProperty = qmlPropNode->correspondingProperty(tree_); correspondingProperty = qmlPropNode->correspondingProperty(tree_);
} }
@ -984,10 +989,15 @@ Node *CppCodeParser::processTopicCommandGroup(const QString& command, const ArgL
bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*'); bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*');
qmlPropNode->setReadOnly(!(writableList || correspondingProperty->isWritable())); qmlPropNode->setReadOnly(!(writableList || correspondingProperty->isWritable()));
} }
}
++argsIter; ++argsIter;
while (argsIter != args.end()) { while (argsIter != args.end()) {
arg = argsIter->first; arg = argsIter->first;
if (splitQmlPropertyArg(arg,type,module,element,property)) { if (splitQmlPropertyArg(arg,type,module,element,property)) {
if (qmlClass->hasProperty(property)) {
location().warning(tr("QML property documented multiple times: '%1'").arg(arg));
}
else {
QmlPropertyNode* qmlPropNode = new QmlPropertyNode(qmlPropGroup, QmlPropertyNode* qmlPropNode = new QmlPropertyNode(qmlPropGroup,
property, property,
type, type,
@ -999,6 +1009,7 @@ Node *CppCodeParser::processTopicCommandGroup(const QString& command, const ArgL
qmlPropNode->setReadOnly(!(writableList || correspondingProperty->isWritable())); qmlPropNode->setReadOnly(!(writableList || correspondingProperty->isWritable()));
} }
} }
}
++argsIter; ++argsIter;
} }
} }

View File

@ -1593,6 +1593,25 @@ FakeNode* FakeNode::lookupQmlModuleNode(Tree* tree, const ArgLocPair& arg)
return fn; return fn;
} }
/*!
Returns true if this QML type or property group contains a
property named \a name.
*/
bool FakeNode::hasProperty(const QString& name) const
{
foreach (Node* child, childNodes()) {
if (child->type() == Node::Fake && child->subType() == Node::QmlPropertyGroup) {
if (child->hasProperty(name))
return true;
}
else if (child->type() == Node::QmlProperty) {
if (child->hasProperty(name))
return true;
}
}
return false;
}
/*! /*!
The constructor calls the FakeNode constructor with The constructor calls the FakeNode constructor with
\a parent, \a name, and Node::Example. \a parent, \a name, and Node::Example.
@ -1614,6 +1633,7 @@ ExampleNode::ExampleNode(InnerNode* parent, const QString& name)
EnumNode::EnumNode(InnerNode *parent, const QString& name) EnumNode::EnumNode(InnerNode *parent, const QString& name)
: LeafNode(Enum, parent, name), ft(0) : LeafNode(Enum, parent, name), ft(0)
{ {
// nothing.
} }
/*! /*!
@ -2362,6 +2382,10 @@ bool QmlPropertyNode::isWritable(Tree* tree)
return true; return true;
} }
/*!
Returns a pointer this QML property's corresponding C++
property, if it has one.
*/
PropertyNode* QmlPropertyNode::correspondingProperty(Tree *tree) PropertyNode* QmlPropertyNode::correspondingProperty(Tree *tree)
{ {
PropertyNode* pn; PropertyNode* pn;
@ -2405,6 +2429,23 @@ PropertyNode* QmlPropertyNode::correspondingProperty(Tree *tree)
return 0; return 0;
} }
/*!
Returns true if this QML type or property group contains a
property named \a name.
*/
bool QmlPropertyNode::hasProperty(const QString& n) const
{
if (name() == n)
return true;
foreach (Node* child, qmlPropNodes()) {
if (child->type() == Node::QmlProperty) {
if (child->name() == n)
return true;
}
}
return false;
}
/*! \class NameCollisionNode /*! \class NameCollisionNode
An instance of this node is inserted in the tree An instance of this node is inserted in the tree

View File

@ -193,6 +193,7 @@ public:
virtual bool isAttached() const { return false; } virtual bool isAttached() const { return false; }
virtual void setAbstract(bool ) { } virtual void setAbstract(bool ) { }
virtual QString title() const { return QString(); } virtual QString title() const { return QString(); }
virtual bool hasProperty(const QString& ) const { return false; }
bool isInternal() const; bool isInternal() const;
bool isIndexNode() const { return indexNodeFlag_; } bool isIndexNode() const { return indexNodeFlag_; }
Type type() const { return nodeType_; } Type type() const { return nodeType_; }
@ -475,6 +476,7 @@ public:
virtual QString nameForLists() const { return title(); } virtual QString nameForLists() const { return title(); }
virtual void setImageFileName(const QString& ) { } virtual void setImageFileName(const QString& ) { }
virtual bool isQmlPropertyGroup() const { return (nodeSubtype_ == QmlPropertyGroup); } virtual bool isQmlPropertyGroup() const { return (nodeSubtype_ == QmlPropertyGroup); }
virtual bool hasProperty(const QString& ) const;
static void insertQmlModuleNode(const QString& qmid, FakeNode* fn); static void insertQmlModuleNode(const QString& qmid, FakeNode* fn);
static FakeNode* lookupQmlModuleNode(Tree* tree, const ArgLocPair& arg); static FakeNode* lookupQmlModuleNode(Tree* tree, const ArgLocPair& arg);
@ -632,6 +634,7 @@ public:
virtual QString qmlModuleName() const { return parent()->qmlModuleName(); } virtual QString qmlModuleName() const { return parent()->qmlModuleName(); }
virtual QString qmlModuleVersion() const { return parent()->qmlModuleVersion(); } virtual QString qmlModuleVersion() const { return parent()->qmlModuleVersion(); }
virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); } virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); }
virtual bool hasProperty(const QString& name) const;
PropertyNode* correspondingProperty(Tree* tree); PropertyNode* correspondingProperty(Tree* tree);