QDBusIntrospection: Add Annotation struct

Add a structure for annotation data containing
name, value and location information. This is done
to be able to emit diagnostics related to annotations
that include source location.

Task-number: QTBUG-2597
Change-Id: Ie990bcd0a16752b5f44f4314f8d730dd1b1a30b4
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Ievgenii Meshcheriakov 2023-06-30 17:02:37 +02:00
parent d625b9bc47
commit 2e8a48c1cd
6 changed files with 71 additions and 43 deletions

View File

@ -38,9 +38,10 @@ public:
struct Interface;
struct Object;
struct ObjectTree;
struct Annotation;
// typedefs
typedef QMap<QString, QString> Annotations;
typedef QMap<QString, Annotation> Annotations;
typedef QList<Argument> Arguments;
typedef QMultiMap<QString, Method> Methods;
typedef QMultiMap<QString, Signal> Signals;
@ -116,6 +117,18 @@ public:
annotations == other.annotations && type == other.type; }
};
struct Annotation
{
SourceLocation location;
QString name;
QString value;
inline bool operator==(const Annotation &other) const
{
return name == other.name && value == other.value;
}
};
struct Interface: public QSharedData
{
SourceLocation location;

View File

@ -148,7 +148,8 @@ QDBusMetaObjectGenerator::findType(const QByteArray &signature,
.arg(id);
// extract from annotations:
QByteArray typeName = annotations.value(annotationName).toLatin1();
auto annotation = annotations.value(annotationName);
QByteArray typeName = annotation.value.toLatin1();
// verify that it's a valid one
if (typeName.isEmpty()) {
@ -158,7 +159,8 @@ QDBusMetaObjectGenerator::findType(const QByteArray &signature,
annotationName += QString::fromLatin1(".%1%2")
.arg(QLatin1StringView(direction))
.arg(id);
typeName = annotations.value(annotationName).toLatin1();
annotation = annotations.value(annotationName);
typeName = annotation.value.toLatin1();
}
if (!typeName.isEmpty()) {
@ -269,7 +271,7 @@ void QDBusMetaObjectGenerator::parseMethods()
prototype.append(')');
// check the async tag
if (m.annotations.value(ANNOTATION_NO_WAIT ""_L1) == "true"_L1)
if (m.annotations.value(ANNOTATION_NO_WAIT ""_L1).value == "true"_L1)
mm.tag = "Q_NOREPLY";
// meta method flags

View File

@ -68,19 +68,23 @@ bool QDBusXmlParser::parseAnnotation(QDBusIntrospection::Annotations &annotation
Q_ASSERT(m_currentInterface);
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "annotation"_L1);
const QXmlStreamAttributes attributes = m_xml.attributes();
const QString name = attributes.value("name"_L1).toString();
QDBusIntrospection::Annotation annotation;
annotation.location = m_currentLocation;
if (!QDBusUtil::isValidInterfaceName(name)) {
const QXmlStreamAttributes attributes = m_xml.attributes();
annotation.name = attributes.value("name"_L1).toString();
if (!QDBusUtil::isValidInterfaceName(annotation.name)) {
qDBusParserError("Invalid D-Bus annotation '%s' found while parsing introspection",
qPrintable(name));
qPrintable(annotation.name));
return false;
}
const QString value = attributes.value("value"_L1).toString();
annotations.insert(name, value);
annotation.value = attributes.value("value"_L1).toString();
annotations.insert(annotation.name, annotation);
if (!interfaceAnnotation)
m_currentInterface->introspection += " "_L1;
m_currentInterface->introspection += " <annotation value=\""_L1 + value.toHtmlEscaped() + "\" name=\""_L1 + name + "\"/>\n"_L1;
m_currentInterface->introspection += " <annotation value=\""_L1
+ annotation.value.toHtmlEscaped() + "\" name=\""_L1 + annotation.name + "\"/>\n"_L1;
return true;
}

View File

@ -235,14 +235,16 @@ static QByteArray qtTypeName(const QString &where, const QString &signature,
QString annotationName = u"org.qtproject.QtDBus.QtTypeName"_s;
if (paramId >= 0)
annotationName += ".%1%2"_L1.arg(QLatin1StringView(direction)).arg(paramId);
QString qttype = annotations.value(annotationName);
auto annotation = annotations.value(annotationName);
QString qttype = annotation.value;
if (!qttype.isEmpty())
return std::move(qttype).toLatin1();
QString oldAnnotationName = u"com.trolltech.QtDBus.QtTypeName"_s;
if (paramId >= 0)
oldAnnotationName += ".%1%2"_L1.arg(QLatin1StringView(direction)).arg(paramId);
qttype = annotations.value(oldAnnotationName);
annotation = annotations.value(oldAnnotationName);
qttype = annotation.value;
if (qttype.isEmpty()) {
fprintf(stderr, "%s: Got unknown type `%s' processing '%s'\n",
@ -372,47 +374,47 @@ static void writeSignalArgList(QTextStream &ts, const QStringList &argNames,
static QString propertyGetter(const QDBusIntrospection::Property &property)
{
QString getter = property.annotations.value("org.qtproject.QtDBus.PropertyGetter"_L1);
if (!getter.isEmpty())
return getter;
auto annotation = property.annotations.value("org.qtproject.QtDBus.PropertyGetter"_L1);
if (!annotation.value.isEmpty())
return annotation.value;
getter = property.annotations.value("com.trolltech.QtDBus.propertyGetter"_L1);
if (!getter.isEmpty()) {
annotation = property.annotations.value("com.trolltech.QtDBus.propertyGetter"_L1);
if (!annotation.value.isEmpty()) {
fprintf(stderr, "%s: Warning: deprecated annotation 'com.trolltech.QtDBus.propertyGetter' found"
" while processing '%s';"
" suggest updating to 'org.qtproject.QtDBus.PropertyGetter'\n",
PROGRAMNAME, qPrintable(inputFile));
return getter;
return annotation.value;
}
getter = property.name;
QString getter = property.name;
getter[0] = getter[0].toLower();
return getter;
}
static QString propertySetter(const QDBusIntrospection::Property &property)
{
QString setter = property.annotations.value("org.qtproject.QtDBus.PropertySetter"_L1);
if (!setter.isEmpty())
return setter;
auto annotation = property.annotations.value("org.qtproject.QtDBus.PropertySetter"_L1);
if (!annotation.value.isEmpty())
return annotation.value;
setter = property.annotations.value("com.trolltech.QtDBus.propertySetter"_L1);
if (!setter.isEmpty()) {
annotation = property.annotations.value("com.trolltech.QtDBus.propertySetter"_L1);
if (!annotation.value.isEmpty()) {
fprintf(stderr, "%s: Warning: deprecated annotation 'com.trolltech.QtDBus.propertySetter' found"
" while processing '%s';"
" suggest updating to 'org.qtproject.QtDBus.PropertySetter'\n",
PROGRAMNAME, qPrintable(inputFile));
return setter;
return annotation.value;
}
setter = "set"_L1 + property.name;
QString setter = "set"_L1 + property.name;
setter[3] = setter[3].toUpper();
return setter;
}
static QString methodName(const QDBusIntrospection::Method &method)
{
QString name = method.annotations.value(u"org.qtproject.QtDBus.MethodName"_s);
QString name = method.annotations.value(u"org.qtproject.QtDBus.MethodName"_s).value;
if (!name.isEmpty())
return name;
@ -588,9 +590,9 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
// methods:
hs << "public Q_SLOTS: // METHODS\n";
for (const QDBusIntrospection::Method &method : interface->methods) {
bool isDeprecated = method.annotations.value("org.freedesktop.DBus.Deprecated"_L1) == "true"_L1;
bool isNoReply =
method.annotations.value(ANNOTATION_NO_WAIT ""_L1) == "true"_L1;
bool isDeprecated = method.annotations.value("org.freedesktop.DBus.Deprecated"_L1).value
== "true"_L1;
bool isNoReply = method.annotations.value(ANNOTATION_NO_WAIT ""_L1).value == "true"_L1;
if (isNoReply && !method.outputArgs.isEmpty()) {
fprintf(stderr, "%s: warning while processing '%s': method %s in interface %s is marked 'no-reply' but has output arguments.\n",
PROGRAMNAME, qPrintable(inputFile), qPrintable(method.name),
@ -687,7 +689,7 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf
hs << "Q_SIGNALS: // SIGNALS\n";
for (const QDBusIntrospection::Signal &signal : interface->signals_) {
hs << " ";
if (signal.annotations.value("org.freedesktop.DBus.Deprecated"_L1) == "true"_L1)
if (signal.annotations.value("org.freedesktop.DBus.Deprecated"_L1).value == "true"_L1)
hs << "Q_DECL_DEPRECATED ";
hs << "void " << signal.name << "(";
@ -921,8 +923,7 @@ static void writeAdaptor(const QString &filename, const QDBusIntrospection::Inte
hs << "public Q_SLOTS: // METHODS\n";
for (const QDBusIntrospection::Method &method : interface->methods) {
bool isNoReply =
method.annotations.value(ANNOTATION_NO_WAIT ""_L1) == "true"_L1;
bool isNoReply = method.annotations.value(ANNOTATION_NO_WAIT ""_L1).value == "true"_L1;
if (isNoReply && !method.outputArgs.isEmpty()) {
fprintf(stderr, "%s: warning while processing '%s': method %s in interface %s is marked 'no-reply' but has output arguments.\n",
PROGRAMNAME, qPrintable(inputFile), qPrintable(method.name), qPrintable(interface->name));

View File

@ -214,7 +214,7 @@ QString printable(const QDBusIntrospection::Method& m)
.arg(arg.type, arg.name);
AnnotationsMap::const_iterator it = m.annotations.begin();
for ( ; it != m.annotations.end(); ++it)
result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value().value);
result += ")";
return result;
@ -228,7 +228,7 @@ QString printable(const QDBusIntrospection::Signal& s)
.arg(arg.type, arg.name);
AnnotationsMap::const_iterator it = s.annotations.begin();
for ( ; it != s.annotations.end(); ++it)
result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value().value);
result += ")";
return result;
@ -247,7 +247,7 @@ QString printable(const QDBusIntrospection::Property& p)
AnnotationsMap::const_iterator it = p.annotations.begin();
for ( ; it != p.annotations.end(); ++it)
result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value());
result += QString("%1 \"%2\", ").arg(it.key()).arg(it.value().value);
return result;
}

View File

@ -32,6 +32,14 @@ private slots:
void properties();
};
static void addAnnotation(QDBusIntrospection::Annotations &annotations, const QString &name,
const QString &value)
{
annotations.insert(
name,
QDBusIntrospection::Annotation{ QDBusIntrospection::SourceLocation{}, name, value });
}
void tst_QDBusXmlParser::initTestCase()
{
// Always initialize the hash seed with a known value to get reliable test results
@ -191,7 +199,7 @@ void tst_QDBusXmlParser::methods_data()
// add a third, with annotations
method.name = "Baz";
method.annotations.insert("foo.testing", "nothing to see here");
addAnnotation(method.annotations, "foo.testing", "nothing to see here");
map << method;
QTest::newRow("method-with-annotation") <<
"<method name=\"Foo\"/>"
@ -290,7 +298,7 @@ void tst_QDBusXmlParser::methods_data()
method = QDBusIntrospection::Method();
method.inputArgs << arg("a{sv}", "variantMap") << arg("u", "index");
method.outputArgs << arg("s", "key") << arg("v", "value");
method.annotations.insert("foo.equivalent", "QVariantMap");
addAnnotation(method.annotations, "foo.equivalent", "QVariantMap");
method.name = "Method2";
map << method;
@ -359,7 +367,7 @@ void tst_QDBusXmlParser::signals__data()
// add a third, with annotations
signal.name = "Baz";
signal.annotations.insert("foo.testing", "nothing to see here");
addAnnotation(signal.annotations, "foo.testing", "nothing to see here");
map << signal;
QTest::newRow("signal-with-annotation") <<
"<signal name=\"Foo\"/>"
@ -408,7 +416,7 @@ void tst_QDBusXmlParser::signals__data()
// with annotation "foo.equivalent":"QVariantMap"
signal = QDBusIntrospection::Signal();
signal.outputArgs << arg("s", "key") << arg("v", "value");
signal.annotations.insert("foo.equivalent", "QVariantMap");
addAnnotation(signal.annotations, "foo.equivalent", "QVariantMap");
signal.name = "Signal2";
map << signal;
@ -492,8 +500,8 @@ void tst_QDBusXmlParser::properties_data()
prop.name = "baz";
prop.type = "as";
prop.access = QDBusIntrospection::Property::Write;
prop.annotations.insert("foo.annotation", "Hello, World");
prop.annotations.insert("foo.annotation2", "Goodbye, World");
addAnnotation(prop.annotations, "foo.annotation", "Hello, World");
addAnnotation(prop.annotations, "foo.annotation2", "Goodbye, World");
map << prop;
QTest::newRow("complex") <<
"<property access=\"read\" type=\"i\" name=\"bar\"/>"