From f3a0422acb6d92be26e30c604b7092a8bfd52b7b Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Fri, 18 May 2012 10:18:57 +0200 Subject: 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 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 --- src/tools/qdoc/cppcodeparser.cpp | 51 ++++++++++++++++++++++++---------------- src/tools/qdoc/node.cpp | 41 ++++++++++++++++++++++++++++++++ src/tools/qdoc/node.h | 3 +++ 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/tools/qdoc/cppcodeparser.cpp b/src/tools/qdoc/cppcodeparser.cpp index d25f5e05e8..b5dcae271c 100644 --- a/src/tools/qdoc/cppcodeparser.cpp +++ b/src/tools/qdoc/cppcodeparser.cpp @@ -960,43 +960,54 @@ Node *CppCodeParser::processTopicCommandGroup(const QString& command, const ArgL QString module; QString element; QString property; + QmlClassNode* qmlClass = 0; bool attached = (command == COMMAND_QMLATTACHEDPROPERTY); ArgList::ConstIterator argsIter = args.begin(); arg = argsIter->first; if (splitQmlPropertyArg(arg,type,module,element,property)) { - QmlClassNode* qmlClass = tree_->findQmlClassNode(module,element); + qmlClass = tree_->findQmlClassNode(module,element); if (qmlClass) { qmlPropGroup = new QmlPropGroupNode(qmlClass,property); //,attached); qmlPropGroup->setLocation(location()); } } if (qmlPropGroup) { - ClassNode *correspondingClass = static_cast(qmlPropGroup->parent())->classNode(); - QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached); - qmlPropNode->setLocation(location()); - qmlPropNode->setQPropertyFlag(); - const PropertyNode *correspondingProperty = 0; - if (correspondingClass) { - correspondingProperty = qmlPropNode->correspondingProperty(tree_); + if (qmlClass->hasProperty(property)) { + location().warning(tr("QML property documented multiple times: '%1'").arg(arg)); } - if (correspondingProperty) { - bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*'); - qmlPropNode->setReadOnly(!(writableList || correspondingProperty->isWritable())); + else { + ClassNode *correspondingClass = static_cast(qmlPropGroup->parent())->classNode(); + QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached); + qmlPropNode->setLocation(location()); + qmlPropNode->setQPropertyFlag(); + + if (correspondingClass) { + correspondingProperty = qmlPropNode->correspondingProperty(tree_); + } + if (correspondingProperty) { + bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*'); + qmlPropNode->setReadOnly(!(writableList || correspondingProperty->isWritable())); + } } ++argsIter; while (argsIter != args.end()) { arg = argsIter->first; if (splitQmlPropertyArg(arg,type,module,element,property)) { - QmlPropertyNode* qmlPropNode = new QmlPropertyNode(qmlPropGroup, - property, - type, - attached); - qmlPropNode->setLocation(location()); - qmlPropNode->setQPropertyFlag(); - if (correspondingProperty) { - bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*'); - qmlPropNode->setReadOnly(!(writableList || correspondingProperty->isWritable())); + if (qmlClass->hasProperty(property)) { + location().warning(tr("QML property documented multiple times: '%1'").arg(arg)); + } + else { + QmlPropertyNode* qmlPropNode = new QmlPropertyNode(qmlPropGroup, + property, + type, + attached); + qmlPropNode->setLocation(location()); + qmlPropNode->setQPropertyFlag(); + if (correspondingProperty) { + bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*'); + qmlPropNode->setReadOnly(!(writableList || correspondingProperty->isWritable())); + } } } ++argsIter; diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index bb70200e7d..fc6dc4e35c 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -1593,6 +1593,25 @@ FakeNode* FakeNode::lookupQmlModuleNode(Tree* tree, const ArgLocPair& arg) 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 \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) : LeafNode(Enum, parent, name), ft(0) { + // nothing. } /*! @@ -2362,6 +2382,10 @@ bool QmlPropertyNode::isWritable(Tree* tree) return true; } +/*! + Returns a pointer this QML property's corresponding C++ + property, if it has one. + */ PropertyNode* QmlPropertyNode::correspondingProperty(Tree *tree) { PropertyNode* pn; @@ -2405,6 +2429,23 @@ PropertyNode* QmlPropertyNode::correspondingProperty(Tree *tree) 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 An instance of this node is inserted in the tree diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h index a7d5f4a891..5a67c72133 100644 --- a/src/tools/qdoc/node.h +++ b/src/tools/qdoc/node.h @@ -193,6 +193,7 @@ public: virtual bool isAttached() const { return false; } virtual void setAbstract(bool ) { } virtual QString title() const { return QString(); } + virtual bool hasProperty(const QString& ) const { return false; } bool isInternal() const; bool isIndexNode() const { return indexNodeFlag_; } Type type() const { return nodeType_; } @@ -475,6 +476,7 @@ public: virtual QString nameForLists() const { return title(); } virtual void setImageFileName(const QString& ) { } virtual bool isQmlPropertyGroup() const { return (nodeSubtype_ == QmlPropertyGroup); } + virtual bool hasProperty(const QString& ) const; static void insertQmlModuleNode(const QString& qmid, FakeNode* fn); static FakeNode* lookupQmlModuleNode(Tree* tree, const ArgLocPair& arg); @@ -632,6 +634,7 @@ public: virtual QString qmlModuleName() const { return parent()->qmlModuleName(); } virtual QString qmlModuleVersion() const { return parent()->qmlModuleVersion(); } virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); } + virtual bool hasProperty(const QString& name) const; PropertyNode* correspondingProperty(Tree* tree); -- cgit v1.2.3