summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTopi Reinio <topi.reinio@qt.io>2020-08-11 11:18:26 +0200
committerTopi Reinio <topi.reinio@qt.io>2020-09-21 12:43:04 +0200
commit5b11b631914ff399d72f0a9b58a7b06e96fc7a6a (patch)
tree6d911b0ea8f19c02b5d7e39f752ef79352c1cc00
parent6e88d94fc5ec05fc5fe2401d91329b096cb801aa (diff)
qdoc: Add support for bindable properties
Add support for the BINDABLE attribute in the Q_PROPERTY macro. The new properties are marked with 'bindable' tag, and the list of access functions/notifier signal is replaced with a descriptive note and a link to QProperty. Read-only properties are also properly marked as such. [ChangeLog][qdoc] The \property command now supports bindable C++ properties that use the new system based on QProperty. Task-number: QTBUG-85565 Change-Id: Ie352b3ce962b6b05a022d444da0991b8a849e474 Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Paul Wicking <paul.wicking@qt.io>
-rw-r--r--src/qdoc/clangcodeparser.cpp52
-rw-r--r--src/qdoc/codemarker.cpp8
-rw-r--r--src/qdoc/docbookgenerator.cpp83
-rw-r--r--src/qdoc/generator.cpp13
-rw-r--r--src/qdoc/generator.h10
-rw-r--r--src/qdoc/htmlgenerator.cpp28
-rw-r--r--src/qdoc/propertynode.cpp3
-rw-r--r--src/qdoc/propertynode.h4
-rw-r--r--src/qdoc/qdocindexfiles.cpp10
9 files changed, 141 insertions, 70 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp
index 9f9dba7e0..36a65c1f1 100644
--- a/src/qdoc/clangcodeparser.cpp
+++ b/src/qdoc/clangcodeparser.cpp
@@ -468,6 +468,9 @@ private:
{
if (symbolName == QLatin1String("QPrivateSignal"))
return true;
+ // Ignore functions generated by property macros
+ if (symbolName.startsWith("_qt_property_"))
+ return true;
return false;
}
@@ -500,7 +503,7 @@ private:
CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc);
CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc, Node **fnNode,
bool &ignoreSignature);
- void parseProperty(const QString &spelling, const Location &loc);
+ bool parseProperty(const QString &spelling, const Location &loc);
void readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cursor);
Aggregate *getSemanticParent(CXCursor cursor);
};
@@ -609,8 +612,7 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
if (!clang_isCursorDefinition(cursor))
return CXChildVisit_Continue;
- if (findNodeForCursor(qdb_,
- cursor)) // Was already parsed, propably in another translation unit
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
return CXChildVisit_Continue;
QString className = fromCXString(clang_getCursorSpelling(cursor));
@@ -679,8 +681,7 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
case CXCursor_Constructor:
case CXCursor_Destructor:
case CXCursor_ConversionFunction: {
- if (findNodeForCursor(qdb_,
- cursor)) // Was already parsed, propably in another translation unit
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
return CXChildVisit_Continue;
QString name = functionName(cursor);
if (ignoredSymbol(name))
@@ -838,9 +839,9 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
}
case CXCursor_FieldDecl:
case CXCursor_VarDecl: {
- if (findNodeForCursor(qdb_,
- cursor)) // Was already parsed, propably in another translation unit
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
return CXChildVisit_Continue;
+
auto access = fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor));
auto var = new VariableNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
var->setAccess(access);
@@ -850,8 +851,7 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
return CXChildVisit_Continue;
}
case CXCursor_TypedefDecl: {
- if (findNodeForCursor(qdb_,
- cursor)) // Was already parsed, propably in another translation unit
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
return CXChildVisit_Continue;
TypedefNode *td = new TypedefNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
td->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
@@ -877,13 +877,10 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
}
default:
if (clang_isDeclaration(kind) && parent_->isClassNode()) {
- // maybe a static_assert (which is not exposed from the clang API)
- QString spelling = getSpelling(clang_getCursorExtent(cursor));
- if (spelling.startsWith(QLatin1String("Q_PROPERTY"))
- || spelling.startsWith(QLatin1String("QDOC_PROPERTY"))
- || spelling.startsWith(QLatin1String("Q_OVERRIDE"))) {
- parseProperty(spelling, fromCXSourceLocation(loc));
- }
+ // may be a property macro or a static_assert
+ // which is not exposed from the clang API
+ parseProperty(getSpelling(clang_getCursorExtent(cursor)),
+ fromCXSourceLocation(loc));
}
return CXChildVisit_Continue;
}
@@ -933,22 +930,31 @@ void ClangVisitor::readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cu
});
}
-void ClangVisitor::parseProperty(const QString &spelling, const Location &loc)
+bool ClangVisitor::parseProperty(const QString &spelling, const Location &loc)
{
+ if (!spelling.startsWith(QLatin1String("Q_PROPERTY"))
+ && !spelling.startsWith(QLatin1String("QDOC_PROPERTY"))
+ && !spelling.startsWith(QLatin1String("Q_OVERRIDE")))
+ return false;
+
int lpIdx = spelling.indexOf(QChar('('));
int rpIdx = spelling.lastIndexOf(QChar(')'));
if (lpIdx <= 0 || rpIdx <= lpIdx)
- return;
+ return false;
+
QString signature = spelling.mid(lpIdx + 1, rpIdx - lpIdx - 1);
signature = signature.simplified();
- QStringList part = signature.split(QChar(' '));
+
+ QStringList part;
+ part = signature.split(QChar(' '));
if (part.first() == QLatin1String("enum"))
part.takeFirst(); // QTBUG-80027
if (part.size() < 2)
- return;
+ return false;
QString type = part.at(0);
QString name = part.at(1);
- if (name.at(0) == QChar('*')) {
+
+ if (name.front() == QChar('*')) {
type.append(QChar('*'));
name.remove(0, 1);
}
@@ -956,6 +962,7 @@ void ClangVisitor::parseProperty(const QString &spelling, const Location &loc)
property->setAccess(Access::Public);
property->setLocation(loc);
property->setDataType(type);
+
int i = 2;
while (i < part.size()) {
QString key = part.at(i++);
@@ -986,6 +993,8 @@ void ClangVisitor::parseProperty(const QString &spelling, const Location &loc)
property->setDesignable(false);
property->setRuntimeDesFunc(value);
}
+ } else if (key == "BINDABLE") {
+ property->setPropertyType(PropertyNode::Bindable);
} else if (key == "RESET") {
qdb_->addPropertyFunction(property, value, PropertyNode::Resetter);
} else if (key == "NOTIFY") {
@@ -1011,6 +1020,7 @@ void ClangVisitor::parseProperty(const QString &spelling, const Location &loc)
}
}
}
+ return true;
}
/*!
diff --git a/src/qdoc/codemarker.cpp b/src/qdoc/codemarker.cpp
index f22dd66bc..546ab3296 100644
--- a/src/qdoc/codemarker.cpp
+++ b/src/qdoc/codemarker.cpp
@@ -188,6 +188,14 @@ QString CodeMarker::extraSynopsis(const Node *node, Section::Style style)
case Node::TypeAlias:
extra << "alias";
break;
+ case Node::Property: {
+ auto propertyNode = static_cast<const PropertyNode *>(node);
+ if (propertyNode->propertyType() == PropertyNode::Bindable)
+ extra << "bindable";
+ if (!propertyNode->isWritable())
+ extra << "read-only";
+ }
+ break;
default:
break;
}
diff --git a/src/qdoc/docbookgenerator.cpp b/src/qdoc/docbookgenerator.cpp
index 0fa640f51..a902db3fc 100644
--- a/src/qdoc/docbookgenerator.cpp
+++ b/src/qdoc/docbookgenerator.cpp
@@ -2150,7 +2150,10 @@ void DocBookGenerator::generateBody(const Node *node)
generateReimplementsClause(fn);
else if (node->isTypeAlias())
generateAddendum(node, TypeAlias, nullptr, false);
-
+ else if (node->isProperty()) {
+ if (static_cast<const PropertyNode *>(node)->propertyType() != PropertyNode::Standard)
+ generateAddendum(node, BindableProperty, nullptr, false);
+ }
if (!generateText(node->doc().body(), node)) {
if (node->isMarkedReimp())
return;
@@ -3512,7 +3515,19 @@ void DocBookGenerator::generateAddendum(const Node *node, Addendum type, CodeMar
newLine();
break;
}
-
+ case BindableProperty:
+ {
+ const Node *linkNode;
+ Atom linkAtom = Atom(Atom::Link, "QProperty");
+ QString link = getAutoLink(&linkAtom, node, &linkNode);
+ writer->writeStartElement(dbNamespace, "para");
+ writer->writeCharacters("This property supports ");
+ generateSimpleLink(link, "QProperty");
+ writer->writeCharacters(" bindings.");
+ writer->writeEndElement(); // para
+ newLine();
+ break;
+ }
default:
break;
}
@@ -3589,41 +3604,43 @@ void DocBookGenerator::generateDetailedMember(const Node *node, const PageNode *
if (node->isProperty()) {
const auto property = static_cast<const PropertyNode *>(node);
- Section section(Section::Accessors, Section::Active);
+ if (property->propertyType() == PropertyNode::Standard) {
+ Section section(Section::Accessors, Section::Active);
- section.appendMembers(property->getters().toVector());
- section.appendMembers(property->setters().toVector());
- section.appendMembers(property->resetters().toVector());
+ section.appendMembers(property->getters().toVector());
+ section.appendMembers(property->setters().toVector());
+ section.appendMembers(property->resetters().toVector());
- if (!section.members().isEmpty()) {
- writer->writeStartElement(dbNamespace, "para");
- newLine();
- writer->writeStartElement(dbNamespace, "emphasis");
- writer->writeAttribute("role", "bold");
- writer->writeCharacters("Access functions:");
- newLine();
- writer->writeEndElement(); // emphasis
- newLine();
- writer->writeEndElement(); // para
- newLine();
- generateSectionList(section, node);
- }
+ if (!section.members().isEmpty()) {
+ writer->writeStartElement(dbNamespace, "para");
+ newLine();
+ writer->writeStartElement(dbNamespace, "emphasis");
+ writer->writeAttribute("role", "bold");
+ writer->writeCharacters("Access functions:");
+ newLine();
+ writer->writeEndElement(); // emphasis
+ newLine();
+ writer->writeEndElement(); // para
+ newLine();
+ generateSectionList(section, node);
+ }
- Section notifiers(Section::Accessors, Section::Active);
- notifiers.appendMembers(property->notifiers().toVector());
+ Section notifiers(Section::Accessors, Section::Active);
+ notifiers.appendMembers(property->notifiers().toVector());
- if (!notifiers.members().isEmpty()) {
- writer->writeStartElement(dbNamespace, "para");
- newLine();
- writer->writeStartElement(dbNamespace, "emphasis");
- writer->writeAttribute("role", "bold");
- writer->writeCharacters("Notifier signal:");
- newLine();
- writer->writeEndElement(); // emphasis
- newLine();
- writer->writeEndElement(); // para
- newLine();
- generateSectionList(notifiers, node);
+ if (!notifiers.members().isEmpty()) {
+ writer->writeStartElement(dbNamespace, "para");
+ newLine();
+ writer->writeStartElement(dbNamespace, "emphasis");
+ writer->writeAttribute("role", "bold");
+ writer->writeCharacters("Notifier signal:");
+ newLine();
+ writer->writeEndElement(); // emphasis
+ newLine();
+ writer->writeEndElement(); // para
+ newLine();
+ generateSectionList(notifiers, node);
+ }
}
} else if (node->isEnumType()) {
const auto en = static_cast<const EnumNode *>(node);
diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp
index 409171fd8..8e9a8698c 100644
--- a/src/qdoc/generator.cpp
+++ b/src/qdoc/generator.cpp
@@ -808,6 +808,10 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
generateReimplementsClause(fn, marker);
else if (node->isTypeAlias())
generateAddendum(node, TypeAlias, marker, false);
+ else if (node->isProperty()) {
+ if (static_cast<const PropertyNode *>(node)->propertyType() != PropertyNode::Standard)
+ generateAddendum(node, BindableProperty, marker);
+ }
if (!generateText(node->doc().body(), node, marker)) {
if (node->isMarkedReimp())
@@ -1419,6 +1423,15 @@ void Generator::generateAddendum(const Node *node, Addendum type, CodeMarker *ma
}
break;
}
+ case BindableProperty:
+ {
+ text << "This property supports "
+ << Atom(Atom::Link, "QProperty")
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << "QProperty"
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ text << " bindings.";
+ break;
+ }
default:
return;
}
diff --git a/src/qdoc/generator.h b/src/qdoc/generator.h
index 39aaf10f3..746ba424a 100644
--- a/src/qdoc/generator.h
+++ b/src/qdoc/generator.h
@@ -55,7 +55,15 @@ class Generator
{
public:
enum ListType { Generic, Obsolete };
- enum Addendum { Invokable, PrivateSignal, QmlSignalHandler, AssociatedProperties, TypeAlias };
+
+ enum Addendum {
+ Invokable,
+ PrivateSignal,
+ QmlSignalHandler,
+ AssociatedProperties,
+ TypeAlias,
+ BindableProperty
+ };
Generator();
virtual ~Generator();
diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp
index 3cc08c84b..dcf5a6d3c 100644
--- a/src/qdoc/htmlgenerator.cpp
+++ b/src/qdoc/htmlgenerator.cpp
@@ -3376,23 +3376,25 @@ void HtmlGenerator::generateDetailedMember(const Node *node, const PageNode *rel
if (node->isProperty()) {
const auto property = static_cast<const PropertyNode *>(node);
- Section section(Section::Accessors, Section::Active);
+ if (property->propertyType() == PropertyNode::Standard) {
+ Section section(Section::Accessors, Section::Active);
- section.appendMembers(property->getters().toVector());
- section.appendMembers(property->setters().toVector());
- section.appendMembers(property->resetters().toVector());
+ section.appendMembers(property->getters().toVector());
+ section.appendMembers(property->setters().toVector());
+ section.appendMembers(property->resetters().toVector());
- if (!section.members().isEmpty()) {
- out() << "<p><b>Access functions:</b></p>\n";
- generateSectionList(section, node, marker);
- }
+ if (!section.members().isEmpty()) {
+ out() << "<p><b>Access functions:</b></p>\n";
+ generateSectionList(section, node, marker);
+ }
- Section notifiers(Section::Accessors, Section::Active);
- notifiers.appendMembers(property->notifiers().toVector());
+ Section notifiers(Section::Accessors, Section::Active);
+ notifiers.appendMembers(property->notifiers().toVector());
- if (!notifiers.members().isEmpty()) {
- out() << "<p><b>Notifier signal:</b></p>\n";
- generateSectionList(notifiers, node, marker);
+ if (!notifiers.members().isEmpty()) {
+ out() << "<p><b>Notifier signal:</b></p>\n";
+ generateSectionList(notifiers, node, marker);
+ }
}
} else if (node->isEnumType()) {
const auto *enumTypeNode = static_cast<const EnumNode *>(node);
diff --git a/src/qdoc/propertynode.cpp b/src/qdoc/propertynode.cpp
index 824ad851a..77a7e249d 100644
--- a/src/qdoc/propertynode.cpp
+++ b/src/qdoc/propertynode.cpp
@@ -83,6 +83,9 @@ void PropertyNode::setOverriddenFrom(const PropertyNode *baseProperty)
*/
QString PropertyNode::qualifiedDataType() const
{
+ if (m_propertyType != Standard)
+ return m_type;
+
if (setters().isEmpty() && resetters().isEmpty()) {
if (m_type.contains(QLatin1Char('*')) || m_type.contains(QLatin1Char('&'))) {
// 'QWidget *' becomes 'QWidget *' const
diff --git a/src/qdoc/propertynode.h b/src/qdoc/propertynode.h
index 5598ab703..be1ccfa09 100644
--- a/src/qdoc/propertynode.h
+++ b/src/qdoc/propertynode.h
@@ -42,6 +42,7 @@ class Aggregate;
class PropertyNode : public Node
{
public:
+ enum PropertyType { Standard, Bindable };
enum FunctionRole { Getter, Setter, Resetter, Notifier };
enum { NumFunctionRoles = Notifier + 1 };
@@ -61,6 +62,7 @@ public:
void setConstant() { m_const = true; }
void setFinal() { m_final = true; }
void setRequired() { m_required = true; }
+ void setPropertyType(PropertyType type) { m_propertyType = type; }
void setRevision(int revision) { m_revision = revision; }
const QString &dataType() const { return m_type; }
@@ -83,6 +85,7 @@ public:
bool isConstant() const { return m_const; }
bool isFinal() const { return m_final; }
bool isRequired() const { return m_required; }
+ PropertyType propertyType() const { return m_propertyType; }
const PropertyNode *overriddenFrom() const { return m_overrides; }
bool storedDefault() const { return true; }
@@ -93,6 +96,7 @@ public:
private:
QString m_type {};
+ PropertyType m_propertyType { Standard };
QString m_runtimeDesFunc {};
QString m_runtimeScrFunc {};
NodeList m_functions[NumFunctionRoles] {};
diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdocindexfiles.cpp
index 22cbef688..2b680b586 100644
--- a/src/qdoc/qdocindexfiles.cpp
+++ b/src/qdoc/qdocindexfiles.cpp
@@ -471,7 +471,10 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
location = Location(parent->name().toLower() + ".html");
} else if (elementName == QLatin1String("property")) {
- node = new PropertyNode(parent, name);
+ PropertyNode *propNode = new PropertyNode(parent, name);
+ node = propNode;
+ if (attributes.value(QLatin1String("bindable")) == QLatin1String("true"))
+ propNode->setPropertyType(PropertyNode::Bindable);
if (!indexUrl.isEmpty())
location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
@@ -1145,7 +1148,10 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
} break;
case Node::Property: {
const auto *propertyNode = static_cast<const PropertyNode *>(node);
- writer.writeAttribute("type", propertyNode->dataType());
+
+ if (propertyNode->propertyType() == PropertyNode::Bindable)
+ writer.writeAttribute("bindable", "true");
+
if (!brief.isEmpty())
writer.writeAttribute("brief", brief);
const auto &getters = propertyNode->getters();