qdoc: QML Inheritance is not resolved correctly.
This change adds some QML property, signal, and method data to the .index file. It also provides more robust resolving of QML inheritance for qml types. Task-number: QTBUG-29778 Change-Id: Iaefd64227913a19f427b21e904ca5e32c82d7b29 Reviewed-by: Jerome Pasion <jerome.pasion@digia.com>
This commit is contained in:
parent
dacc222d5a
commit
2cb22c6cc0
@ -1287,17 +1287,15 @@ QList<Section> CppCodeMarker::qmlSections(const QmlClassNode* qmlClassNode, Syno
|
||||
}
|
||||
++c;
|
||||
}
|
||||
const DocNode* dn = current->qmlBaseNode();
|
||||
if (dn) {
|
||||
if (dn->subType() == Node::QmlClass)
|
||||
current = static_cast<const QmlClassNode*>(dn);
|
||||
else {
|
||||
dn->doc().location().warning(tr("Base class of QML class '%1' is ambgiguous").arg(current->name()));
|
||||
current = 0;
|
||||
}
|
||||
current = current->qmlBaseNode();
|
||||
while (current) {
|
||||
if (current->isAbstract())
|
||||
break;
|
||||
if (current->isInternal())
|
||||
current = current->qmlBaseNode();
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
current = 0;
|
||||
}
|
||||
append(sections, all, true);
|
||||
}
|
||||
|
@ -4198,7 +4198,10 @@ void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker*
|
||||
{
|
||||
if (!qcn)
|
||||
return;
|
||||
const DocNode* base = qcn->qmlBaseNode();
|
||||
const QmlClassNode* base = qcn->qmlBaseNode();
|
||||
while (base && base->isInternal()) {
|
||||
base = base->qmlBaseNode();
|
||||
}
|
||||
if (base) {
|
||||
writeStartTag(DT_qmlInherits);
|
||||
//writeStartTag(DT_qmlTypeDef);
|
||||
|
@ -3870,7 +3870,10 @@ void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* mar
|
||||
{
|
||||
if (!qcn)
|
||||
return;
|
||||
const DocNode* base = qcn->qmlBaseNode();
|
||||
const QmlClassNode* base = qcn->qmlBaseNode();
|
||||
while (base && base->isInternal()) {
|
||||
base = base->qmlBaseNode();
|
||||
}
|
||||
if (base) {
|
||||
Text text;
|
||||
text << Atom::ParaLeft << "Inherits ";
|
||||
|
@ -2161,10 +2161,13 @@ void QmlClassNode::subclasses(const QString& base, NodeList& subs)
|
||||
This function splits \a arg on the blank character to get a
|
||||
QML module name and version number. It then spilts the version
|
||||
number on the '.' character to get a major version number and
|
||||
a minor vrsion number. Both version numbers must be present.
|
||||
It stores these components separately. If all three are found,
|
||||
true is returned. If any of the three is not found or is not
|
||||
correct, false is returned.
|
||||
a minor vrsion number. Both major the major and minor version
|
||||
numbers should be present, but the minor version number is not
|
||||
absolutely necessary.
|
||||
|
||||
It stores the three components separately in this node. If all
|
||||
three are found, true is returned. If any of the three is not
|
||||
found or is not in the correct format, false is returned.
|
||||
*/
|
||||
bool Node::setQmlModuleInfo(const QString& arg)
|
||||
{
|
||||
|
@ -65,7 +65,6 @@ typedef QList<Node*> NodeList;
|
||||
typedef QMap<QString, Node*> NodeMap;
|
||||
typedef QMultiMap<QString, Node*> NodeMultiMap;
|
||||
typedef QMultiMap<QString, const ExampleNode*> ExampleNodeMap;
|
||||
typedef QList<QPair<QString,QString> > ImportList;
|
||||
|
||||
class Node
|
||||
{
|
||||
@ -206,7 +205,7 @@ public:
|
||||
virtual bool hasProperty(const QString& ) const { return false; }
|
||||
virtual void getMemberNamespaces(NodeMap& ) { }
|
||||
virtual void getMemberClasses(NodeMap& ) { }
|
||||
bool isInternal() const;
|
||||
virtual bool isInternal() const;
|
||||
bool isIndexNode() const { return indexNodeFlag_; }
|
||||
bool wasSeen() const { return seen_; }
|
||||
Type type() const { return nodeType_; }
|
||||
@ -530,6 +529,26 @@ private:
|
||||
QString imageFileName_;
|
||||
};
|
||||
|
||||
struct ImportRec {
|
||||
QString name_; // module name
|
||||
QString version_; // <major> . <minor>
|
||||
QString importId_; // "as" name
|
||||
QString importUri_; // subdirectory of module directory
|
||||
|
||||
ImportRec(const QString& name,
|
||||
const QString& version,
|
||||
const QString& importId,
|
||||
const QString& importUri)
|
||||
: name_(name), version_(version), importId_(importId), importUri_(importUri) { }
|
||||
QString& name() { return name_; }
|
||||
QString& version() { return version_; }
|
||||
QString& importId() { return importId_; }
|
||||
QString& importUri() { return importUri_; }
|
||||
bool isEmpty() const { return name_.isEmpty(); }
|
||||
};
|
||||
|
||||
typedef QList<ImportRec> ImportList;
|
||||
|
||||
class QmlClassNode : public DocNode
|
||||
{
|
||||
public:
|
||||
@ -543,12 +562,13 @@ public:
|
||||
virtual void clearCurrentChild();
|
||||
virtual bool isAbstract() const { return abstract_; }
|
||||
virtual void setAbstract(bool b) { abstract_ = b; }
|
||||
virtual bool isInternal() const { return (status() == Internal); }
|
||||
const ImportList& importList() const { return importList_; }
|
||||
void setImportList(const ImportList& il) { importList_ = il; }
|
||||
const QString& qmlBaseName() const { return baseName_; }
|
||||
void setQmlBaseName(const QString& name) { baseName_ = name; }
|
||||
const DocNode* qmlBaseNode() const { return baseNode_; }
|
||||
void setQmlBaseNode(DocNode* b) { baseNode_ = b; }
|
||||
const QmlClassNode* qmlBaseNode() const { return baseNode_; }
|
||||
void setQmlBaseNode(QmlClassNode* b) { baseNode_ = b; }
|
||||
void requireCppClass() { cnodeRequired_ = true; }
|
||||
bool cppClassRequired() const { return cnodeRequired_; }
|
||||
static void addInheritedBy(const QString& base, Node* sub);
|
||||
@ -564,7 +584,7 @@ private:
|
||||
bool cnodeRequired_;
|
||||
ClassNode* cnode_;
|
||||
QString baseName_;
|
||||
DocNode* baseNode_;
|
||||
QmlClassNode* baseNode_;
|
||||
ImportList importList_;
|
||||
};
|
||||
|
||||
|
@ -291,18 +291,29 @@ DocNode* QDocDatabase::addToModule(const QString& name, Node* node)
|
||||
*/
|
||||
DocNode* QDocDatabase::addToQmlModule(const QString& name, Node* node)
|
||||
{
|
||||
QString longQmid, shortQmid;
|
||||
QStringList dotSplit;
|
||||
QStringList blankSplit = name.split(QLatin1Char(' '));
|
||||
if (blankSplit.size() > 1) {
|
||||
longQmid = blankSplit[0] + blankSplit[1];
|
||||
dotSplit = blankSplit[1].split(QLatin1Char('.'));
|
||||
shortQmid = blankSplit[0] + dotSplit[0];
|
||||
}
|
||||
DocNode* dn = findQmlModule(name);
|
||||
dn->addMember(node);
|
||||
node->setQmlModuleInfo(name);
|
||||
if (node->subType() == Node::QmlClass) {
|
||||
QString t = node->qmlModuleIdentifier() + "::" + node->name();
|
||||
QmlClassNode* n = static_cast<QmlClassNode*>(node);
|
||||
if (!qmlTypeMap_.contains(t))
|
||||
qmlTypeMap_.insert(t,n);
|
||||
if (!masterMap_.contains(t))
|
||||
masterMap_.insert(t,node);
|
||||
if (!masterMap_.contains(node->name(),node))
|
||||
masterMap_.insert(node->name(),node);
|
||||
QString key = longQmid + "::" + node->name();
|
||||
for (int i=0; i<2; ++i) {
|
||||
if (!qmlTypeMap_.contains(key))
|
||||
qmlTypeMap_.insert(key,n);
|
||||
if (!masterMap_.contains(key))
|
||||
masterMap_.insert(key,node);
|
||||
if (!masterMap_.contains(node->name(),node))
|
||||
masterMap_.insert(node->name(),node);
|
||||
key = shortQmid + "::" + node->name();
|
||||
}
|
||||
}
|
||||
return dn;
|
||||
}
|
||||
@ -332,7 +343,45 @@ QmlClassNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Looks up the QML type node identified by the Qml module id
|
||||
constructed from the strings in the \a import record and the
|
||||
QML type \a name and returns a pointer to the QML type node.
|
||||
If a QML type node is not found, 0 is returned.
|
||||
*/
|
||||
QmlClassNode* QDocDatabase::findQmlType(const ImportRec& import, const QString& name) const
|
||||
{
|
||||
if (!import.isEmpty()) {
|
||||
QStringList dotSplit;
|
||||
dotSplit = name.split(QLatin1Char('.'));
|
||||
QString qmName;
|
||||
if (import.importUri_.isEmpty())
|
||||
qmName = import.name_;
|
||||
else
|
||||
qmName = import.importUri_;
|
||||
for (int i=0; i<dotSplit.size(); ++i) {
|
||||
QString qmid = qmName + import.version_;
|
||||
QString qualifiedName = qmid + "::" + dotSplit[i];
|
||||
QmlClassNode* qcn = qmlTypeMap_.value(qualifiedName);
|
||||
if (qcn) {
|
||||
return qcn;
|
||||
}
|
||||
if (import.version_.size() > 1) {
|
||||
int dot = import.version_.lastIndexOf(QChar('.'));
|
||||
if (dot > 0) {
|
||||
qmid = import.name_ + import.version_.left(dot);
|
||||
qualifiedName = qmid + "::" + dotSplit[i];
|
||||
qcn = qmlTypeMap_.value(qualifiedName);
|
||||
if (qcn) {
|
||||
return qcn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -836,14 +885,23 @@ void QDocDatabase::resolveQmlInheritance(InnerNode* root)
|
||||
if (child->type() == Node::Document && child->subType() == Node::QmlClass) {
|
||||
QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
|
||||
if ((qcn->qmlBaseNode() == 0) && !qcn->qmlBaseName().isEmpty()) {
|
||||
QmlClassNode* bqcn = findQmlType(QString(), qcn->qmlBaseName());
|
||||
QmlClassNode* bqcn = 0;
|
||||
const ImportList& imports = qcn->importList();
|
||||
for (int i=0; i<imports.size(); ++i) {
|
||||
bqcn = findQmlType(imports[i], qcn->qmlBaseName());
|
||||
if (bqcn)
|
||||
break;
|
||||
}
|
||||
if (bqcn == 0) {
|
||||
bqcn = findQmlType(QString(), qcn->qmlBaseName());
|
||||
}
|
||||
if (bqcn) {
|
||||
qcn->setQmlBaseNode(bqcn);
|
||||
}
|
||||
#if 0
|
||||
else {
|
||||
qDebug() << "Unable to resolve QML base type:" << qcn->qmlBaseName()
|
||||
<< "for QML type:" << qcn->name();
|
||||
qDebug() << "Temporary error message (ignore): UNABLE to resolve QML base type:"
|
||||
<< qcn->qmlBaseName() << "for QML type:" << qcn->name();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -851,6 +909,24 @@ void QDocDatabase::resolveQmlInheritance(InnerNode* root)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void QDocDatabase::resolveQmlInheritance(InnerNode* root)
|
||||
{
|
||||
// Dop we need recursion?
|
||||
foreach (Node* child, root->childNodes()) {
|
||||
if (child->type() == Node::Document && child->subType() == Node::QmlClass) {
|
||||
QmlClassNode* qcn = static_cast<QmlClassNode*>(child);
|
||||
if ((qcn->qmlBaseNode() == 0) && !qcn->qmlBaseName().isEmpty()) {
|
||||
QmlClassNode* bqcn = findQmlType(QString(), qcn->qmlBaseName());
|
||||
if (bqcn) {
|
||||
qcn->setQmlBaseNode(bqcn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
*/
|
||||
void QDocDatabase::resolveTargets(InnerNode* root)
|
||||
|
@ -107,6 +107,7 @@ class QDocDatabase
|
||||
DocNode* addToQmlModule(const QString& name, Node* node);
|
||||
|
||||
QmlClassNode* findQmlType(const QString& qmid, const QString& name) const;
|
||||
QmlClassNode* findQmlType(const ImportRec& import, const QString& name) const;
|
||||
|
||||
void findAllClasses(const InnerNode *node);
|
||||
void findAllFunctions(const InnerNode *node);
|
||||
|
@ -190,6 +190,9 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element,
|
||||
((element.nodeName() == "page") && (element.attribute("subtype") == "qmlclass"))) {
|
||||
QmlClassNode* qcn = new QmlClassNode(parent, name);
|
||||
qcn->setTitle(element.attribute("title"));
|
||||
QString qmlModuleName = element.attribute("qml-module-name");
|
||||
QString qmlModuleVersion = element.attribute("qml-module-version");
|
||||
qdb_->addToQmlModule(qmlModuleName + " " + qmlModuleVersion, qcn);
|
||||
if (element.hasAttribute("location"))
|
||||
name = element.attribute("location", QString());
|
||||
if (!indexUrl.isEmpty())
|
||||
@ -209,6 +212,31 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element,
|
||||
location = Location(name);
|
||||
node = qbtn;
|
||||
}
|
||||
else if (element.nodeName() == "qmlproperty") {
|
||||
QmlClassNode* qcn = static_cast<QmlClassNode*>(parent);
|
||||
QString type = element.attribute("type");
|
||||
bool attached = false;
|
||||
if (element.attribute("attached") == "true")
|
||||
attached = true;
|
||||
bool readonly = false;
|
||||
if (element.attribute("writable") == "false")
|
||||
readonly = true;
|
||||
QmlPropertyNode* qpn = new QmlPropertyNode(qcn, name, type, attached);
|
||||
qpn->setReadOnly(readonly);
|
||||
node = qpn;
|
||||
}
|
||||
else if ((element.nodeName() == "qmlmethod") ||
|
||||
(element.nodeName() == "qmlsignal") ||
|
||||
(element.nodeName() == "qmlsignalhandler")) {
|
||||
Node::Type t = Node::QmlMethod;
|
||||
if (element.nodeName() == "qmlsignal")
|
||||
t = Node::QmlSignal;
|
||||
else if (element.nodeName() == "qmlsignalhandler")
|
||||
t = Node::QmlSignalHandler;
|
||||
bool attached = false;
|
||||
FunctionNode* fn = new FunctionNode(t, parent, name, attached);
|
||||
node = fn;
|
||||
}
|
||||
else if (element.nodeName() == "page") {
|
||||
Node::SubType subtype;
|
||||
Node::PageType ptype = Node::NoPageType;
|
||||
@ -476,17 +504,22 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element,
|
||||
InnerNode* inner = static_cast<InnerNode*>(node);
|
||||
QDomElement child = element.firstChildElement();
|
||||
while (!child.isNull()) {
|
||||
if (element.nodeName() == "class")
|
||||
if (element.nodeName() == "class") {
|
||||
readIndexSection(child, inner, indexUrl);
|
||||
else if (element.nodeName() == "qmlclass")
|
||||
}
|
||||
else if (element.nodeName() == "qmlclass") {
|
||||
readIndexSection(child, inner, indexUrl);
|
||||
else if (element.nodeName() == "page")
|
||||
}
|
||||
else if (element.nodeName() == "page") {
|
||||
readIndexSection(child, inner, indexUrl);
|
||||
else if (element.nodeName() == "namespace" && !name.isEmpty())
|
||||
}
|
||||
else if (element.nodeName() == "namespace" && !name.isEmpty()) {
|
||||
// The root node in the index is a namespace with an empty name.
|
||||
readIndexSection(child, inner, indexUrl);
|
||||
else
|
||||
}
|
||||
else {
|
||||
readIndexSection(child, parent, indexUrl);
|
||||
}
|
||||
child = child.nextSiblingElement();
|
||||
}
|
||||
}
|
||||
@ -562,6 +595,8 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
|
||||
return false;
|
||||
|
||||
QString nodeName;
|
||||
QString qmlModuleName;
|
||||
QString qmlModuleVersion;
|
||||
switch (node->type()) {
|
||||
case Node::Namespace:
|
||||
nodeName = "namespace";
|
||||
@ -571,8 +606,11 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
|
||||
break;
|
||||
case Node::Document:
|
||||
nodeName = "page";
|
||||
if (node->subType() == Node::QmlClass)
|
||||
if (node->subType() == Node::QmlClass) {
|
||||
nodeName = "qmlclass";
|
||||
qmlModuleName = node->qmlModuleName();
|
||||
qmlModuleVersion = node->qmlModuleVersion();
|
||||
}
|
||||
else if (node->subType() == Node::QmlBasicType)
|
||||
nodeName = "qmlbasictype";
|
||||
break;
|
||||
@ -687,6 +725,10 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
|
||||
writer.writeAttribute("status", status);
|
||||
|
||||
writer.writeAttribute("name", objName);
|
||||
if (!qmlModuleName.isEmpty()) {
|
||||
writer.writeAttribute("qml-module-name", qmlModuleName);
|
||||
writer.writeAttribute("qml-module-version", qmlModuleVersion);
|
||||
}
|
||||
QString fullName = node->fullDocumentName();
|
||||
if (fullName != objName)
|
||||
writer.writeAttribute("fullname", fullName);
|
||||
|
@ -373,6 +373,24 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation,
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Reconstruct the qualified \a id using dot notation
|
||||
and return the fully qualified string.
|
||||
*/
|
||||
QString QmlDocVisitor::getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id)
|
||||
{
|
||||
QString result;
|
||||
if (id) {
|
||||
result = id->name.toString();
|
||||
id = id->next;
|
||||
while (id != 0) {
|
||||
result += QChar('.') + id->name.toString();
|
||||
id = id->next;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Begin the visit of the object \a definition, recording it in the
|
||||
qdoc database. Increment the object nesting level, which is used
|
||||
@ -381,7 +399,7 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation,
|
||||
*/
|
||||
bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition)
|
||||
{
|
||||
QString type = definition->qualifiedTypeNameId->name.toString();
|
||||
QString type = getFullyQualifiedId(definition->qualifiedTypeNameId);
|
||||
nestingLevel++;
|
||||
|
||||
if (current->type() == Node::Namespace) {
|
||||
@ -420,17 +438,18 @@ void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *definition)
|
||||
*/
|
||||
bool QmlDocVisitor::visit(QQmlJS::AST::UiImportList *imports)
|
||||
{
|
||||
QQmlJS::AST::UiImport* imp = imports->import;
|
||||
quint32 length = imp->versionToken.offset - imp->fileNameToken.offset - 1;
|
||||
QString module = document.mid(imp->fileNameToken.offset,length);
|
||||
QString version = document.mid(imp->versionToken.offset, imp->versionToken.length);
|
||||
if (version.size() > 1) {
|
||||
int dot = version.lastIndexOf(QChar('.'));
|
||||
if (dot > 0)
|
||||
version = version.left(dot);
|
||||
}
|
||||
importList.append(QPair<QString, QString>(module, version));
|
||||
while (imports != 0) {
|
||||
QQmlJS::AST::UiImport* imp = imports->import;
|
||||
|
||||
QString name = document.mid(imp->fileNameToken.offset, imp->fileNameToken.length);
|
||||
if (name[0] == '\"')
|
||||
name = name.mid(1, name.length()-2);
|
||||
QString version = document.mid(imp->versionToken.offset, imp->versionToken.length);
|
||||
QString importId = document.mid(imp->importIdToken.offset, imp->importIdToken.length);
|
||||
QString importUri = getFullyQualifiedId(imp->importUri);
|
||||
importList.append(ImportRec(name, version, importId, importUri));
|
||||
imports = imports->next;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -97,6 +97,7 @@ public:
|
||||
void endVisit(QQmlJS::AST::UiQualifiedId *);
|
||||
|
||||
private:
|
||||
QString getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id);
|
||||
QQmlJS::AST::SourceLocation precedingComment(quint32 offset) const;
|
||||
bool applyDocumentation(QQmlJS::AST::SourceLocation location, Node *node);
|
||||
void applyMetacommands(QQmlJS::AST::SourceLocation location, Node* node, Doc& doc);
|
||||
@ -110,7 +111,7 @@ private:
|
||||
QString filePath;
|
||||
QString name;
|
||||
QString document;
|
||||
QList<QPair<QString, QString> > importList;
|
||||
ImportList importList;
|
||||
QSet<QString> commands;
|
||||
QSet<QString> topics;
|
||||
QSet<quint32> usedComments;
|
||||
|
Loading…
Reference in New Issue
Block a user