qdbusxml2cpp: Improve error reporting

Use this format when reporting diagnostics relating to a source file:

   <file name>:<line>:<column>: {error|warning|note}: <message>

This makes it easier to find the source elements that caused
a diagnostics report.

Fixes: QTBUG-2597
Change-Id: I8d8d13f7d71d1ce0c5050a0d08dddd33f9997f27
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ievgenii Meshcheriakov 2023-06-30 17:03:47 +02:00
parent 2f82ba66e2
commit b48a588e13
2 changed files with 132 additions and 63 deletions

View File

@ -33,6 +33,27 @@ public:
int run(const QCoreApplication &app);
private:
class DiagnosticsReporter final : public QDBusIntrospection::DiagnosticsReporter
{
public:
void setFileName(const QString &fileName) { m_fileName = fileName; }
bool hadErrors() const { return m_hadErrors; }
void warning(const QDBusIntrospection::SourceLocation &location, const char *msg,
...) override;
void error(const QDBusIntrospection::SourceLocation &location, const char *msg,
...) override;
void note(const QDBusIntrospection::SourceLocation &location, const char *msg, ...)
Q_ATTRIBUTE_FORMAT_PRINTF(3, 4);
private:
QString m_fileName;
bool m_hadErrors = false;
void report(const QDBusIntrospection::SourceLocation &location, const char *msg, va_list ap,
const char *severity);
};
enum ClassType { Proxy, Adaptor };
void writeAdaptor(const QString &filename, const QDBusIntrospection::Interfaces &interfaces);
@ -42,7 +63,8 @@ private:
void cleanInterfaces(QDBusIntrospection::Interfaces &interfaces);
QTextStream &writeHeader(QTextStream &ts, bool changesWillBeLost);
QString classNameForInterface(const QString &interface, ClassType classType);
QByteArray qtTypeName(const QString &where, const QString &signature,
QByteArray qtTypeName(const QDBusIntrospection::SourceLocation &location,
const QString &signature,
const QDBusIntrospection::Annotations &annotations,
qsizetype paramId = -1, const char *direction = "Out");
void
@ -65,6 +87,8 @@ private:
QStringList includes;
QStringList globalIncludes;
QStringList wantedInterfaces;
DiagnosticsReporter reporter;
};
static const char includeList[] =
@ -78,26 +102,60 @@ static const char includeList[] =
static const char forwardDeclarations[] =
"#include <QtCore/qcontainerfwd.h>\n";
void QDBusXmlToCpp::DiagnosticsReporter::warning(const QDBusIntrospection::SourceLocation &location,
const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
report(location, msg, ap, "warning");
va_end(ap);
}
void QDBusXmlToCpp::DiagnosticsReporter::error(const QDBusIntrospection::SourceLocation &location,
const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
report(location, msg, ap, "error");
va_end(ap);
m_hadErrors = true;
}
void QDBusXmlToCpp::DiagnosticsReporter::note(const QDBusIntrospection::SourceLocation &location,
const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
report(location, msg, ap, "note");
va_end(ap);
m_hadErrors = true;
}
void QDBusXmlToCpp::DiagnosticsReporter::report(const QDBusIntrospection::SourceLocation &location,
const char *msg, va_list ap, const char *severity)
{
fprintf(stderr, "%s:%lld:%lld: %s: ", qPrintable(m_fileName),
(long long int)location.lineNumber, (long long int)location.columnNumber + 1, severity);
vfprintf(stderr, msg, ap);
}
QDBusIntrospection::Interfaces QDBusXmlToCpp::readInput()
{
QFile input(inputFile);
if (inputFile.isEmpty() || inputFile == "-"_L1)
if (inputFile.isEmpty() || inputFile == "-"_L1) {
reporter.setFileName("<standard input>"_L1);
input.open(stdin, QIODevice::ReadOnly);
else
} else {
reporter.setFileName(inputFile);
input.open(QIODevice::ReadOnly);
}
QByteArray data = input.readAll();
auto interfaces = QDBusIntrospection::parseInterfaces(QString::fromUtf8(data), &reporter);
if (reporter.hadErrors())
exit(1);
// check if the input is already XML
data = data.trimmed();
if (data.startsWith("<!DOCTYPE ") || data.startsWith("<?xml") ||
data.startsWith("<node") || data.startsWith("<interface"))
// already XML
return QDBusIntrospection::parseInterfaces(QString::fromUtf8(data));
fprintf(stderr, "%s: Cannot process input: '%s'. Stop.\n",
PROGRAMNAME, qPrintable(inputFile));
exit(1);
return interfaces;
}
void QDBusXmlToCpp::cleanInterfaces(QDBusIntrospection::Interfaces &interfaces)
@ -253,7 +311,8 @@ QString QDBusXmlToCpp::classNameForInterface(const QString &interface,
return retval;
}
QByteArray QDBusXmlToCpp::qtTypeName(const QString &where, const QString &signature,
QByteArray QDBusXmlToCpp::qtTypeName(const QDBusIntrospection::SourceLocation &location,
const QString &signature,
const QDBusIntrospection::Annotations &annotations,
qsizetype paramId, const char *direction)
{
@ -274,20 +333,17 @@ QByteArray QDBusXmlToCpp::qtTypeName(const QString &where, const QString &signat
qttype = annotation.value;
if (qttype.isEmpty()) {
fprintf(stderr, "%s: Got unknown type `%s' processing '%s'\n",
PROGRAMNAME, qPrintable(signature), qPrintable(inputFile));
fprintf(stderr,
"You should add <annotation name=\"%s\" value=\"<type>\"/> to the XML "
"description for '%s'\n",
qPrintable(annotationName), qPrintable(where));
reporter.error(location, "unknown type `%s'\n", qPrintable(signature));
reporter.note(location, "you should add <annotation name=\"%s\" value=\"<type>\"/>\n",
qPrintable(annotationName));
exit(1);
}
fprintf(stderr, "%s: Warning: deprecated annotation '%s' found while processing '%s'; "
"suggest updating to '%s'\n",
PROGRAMNAME, qPrintable(oldAnnotationName), qPrintable(inputFile),
qPrintable(annotationName));
reporter.warning(annotation.location, "deprecated annotation '%s' found\n",
qPrintable(oldAnnotationName));
reporter.note(annotation.location, "suggest updating to '%s'\n",
qPrintable(annotationName));
return std::move(qttype).toLatin1();
}
@ -358,7 +414,7 @@ void QDBusXmlToCpp::writeArgList(QTextStream &ts, const QStringList &argNames,
qsizetype argPos = 0;
for (qsizetype i = 0; i < inputArgs.size(); ++i) {
const QDBusIntrospection::Argument &arg = inputArgs.at(i);
QString type = constRefArg(qtTypeName(arg.name, arg.type, annotations, i, "In"));
QString type = constRefArg(qtTypeName(arg.location, arg.type, annotations, i, "In"));
if (!first)
ts << ", ";
@ -375,7 +431,7 @@ void QDBusXmlToCpp::writeArgList(QTextStream &ts, const QStringList &argNames,
if (!first)
ts << ", ";
ts << nonConstRefArg(qtTypeName(arg.name, arg.type, annotations, i, "Out"))
ts << nonConstRefArg(qtTypeName(arg.location, arg.type, annotations, i, "Out"))
<< argNames.at(argPos++);
first = false;
}
@ -389,8 +445,7 @@ void QDBusXmlToCpp::writeSignalArgList(QTextStream &ts, const QStringList &argNa
qsizetype argPos = 0;
for (qsizetype i = 0; i < outputArgs.size(); ++i) {
const QDBusIntrospection::Argument &arg = outputArgs.at(i);
QString type = constRefArg(
qtTypeName(arg.name, arg.type, annotations, i, "Out"));
QString type = constRefArg(qtTypeName(arg.location, arg.type, annotations, i, "Out"));
if (!first)
ts << ", ";
@ -407,10 +462,10 @@ QString QDBusXmlToCpp::propertyGetter(const QDBusIntrospection::Property &proper
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));
reporter.warning(annotation.location,
"deprecated annotation 'com.trolltech.QtDBus.propertyGetter' found\n");
reporter.note(annotation.location,
"suggest updating to 'org.qtproject.QtDBus.PropertyGetter'\n");
return annotation.value;
}
@ -427,10 +482,10 @@ QString QDBusXmlToCpp::propertySetter(const QDBusIntrospection::Property &proper
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));
reporter.warning(annotation.location,
"deprecated annotation 'com.trolltech.QtDBus.propertySetter' found\n");
reporter.note(annotation.location,
"suggest updating to 'org.qtproject.QtDBus.PropertySetter'\n");
return annotation.value;
}
@ -580,7 +635,7 @@ void QDBusXmlToCpp::writeProxy(const QString &filename,
// properties:
for (const QDBusIntrospection::Property &property : interface->properties) {
QByteArray type = qtTypeName(property.name, property.type, property.annotations);
QByteArray type = qtTypeName(property.location, property.type, property.annotations);
QString getter = propertyGetter(property);
QString setter = propertySetter(property);
@ -622,9 +677,10 @@ void QDBusXmlToCpp::writeProxy(const QString &filename,
== "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));
reporter.warning(method.location,
"method %s in interface %s is marked 'no-reply' but has output "
"arguments.\n",
qPrintable(method.name), qPrintable(interface->name));
continue;
}
@ -639,8 +695,9 @@ void QDBusXmlToCpp::writeProxy(const QString &filename,
hs << "inline QDBusPendingReply<";
for (qsizetype i = 0; i < method.outputArgs.size(); ++i)
hs << (i > 0 ? ", " : "")
<< templateArg(qtTypeName(method.outputArgs.at(i).name, method.outputArgs.at(i).type,
method.annotations, i, "Out"));
<< templateArg(qtTypeName(method.outputArgs.at(i).location,
method.outputArgs.at(i).type, method.annotations,
i, "Out"));
hs << "> ";
}
@ -673,8 +730,9 @@ void QDBusXmlToCpp::writeProxy(const QString &filename,
if (method.outputArgs.size() > 1) {
// generate the old-form QDBusReply methods with multiple incoming parameters
hs << (isDeprecated ? " Q_DECL_DEPRECATED " : " ") << "inline QDBusReply<"
<< templateArg(qtTypeName(method.outputArgs.first().name, method.outputArgs.first().type,
method.annotations, 0, "Out"))
<< templateArg(qtTypeName(method.outputArgs.first().location,
method.outputArgs.first().type, method.annotations, 0,
"Out"))
<< "> ";
hs << method.name << "(";
@ -703,8 +761,9 @@ void QDBusXmlToCpp::writeProxy(const QString &filename,
// yes, starting from 1
for (qsizetype i = 1; i < method.outputArgs.size(); ++i)
hs << " " << argNames.at(argPos++) << " = qdbus_cast<"
<< templateArg(qtTypeName(method.outputArgs.at(i).name, method.outputArgs.at(i).type,
method.annotations, i, "Out"))
<< templateArg(qtTypeName(method.outputArgs.at(i).location,
method.outputArgs.at(i).type, method.annotations,
i, "Out"))
<< ">(reply.arguments().at(" << i << "));\n";
hs << " }\n"
" return reply;\n"
@ -911,7 +970,7 @@ void QDBusXmlToCpp::writeAdaptor(const QString &filename,
hs << "public: // PROPERTIES\n";
for (const QDBusIntrospection::Property &property : interface->properties) {
QByteArray type = qtTypeName(property.name, property.type, property.annotations);
QByteArray type = qtTypeName(property.location, property.type, property.annotations);
QString constRefType = constRefArg(type);
QString getter = propertyGetter(property);
QString setter = propertySetter(property);
@ -954,8 +1013,10 @@ void QDBusXmlToCpp::writeAdaptor(const QString &filename,
for (const QDBusIntrospection::Method &method : interface->methods) {
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));
reporter.warning(method.location,
"method %s in interface %s is marked 'no-reply' but has output "
"arguments.\n",
qPrintable(method.name), qPrintable(interface->name));
continue;
}
@ -968,8 +1029,9 @@ void QDBusXmlToCpp::writeAdaptor(const QString &filename,
hs << "void ";
cs << "void ";
} else {
returnType = qtTypeName(method.outputArgs.first().name, method.outputArgs.first().type,
method.annotations, 0, "Out");
returnType =
qtTypeName(method.outputArgs.first().location,
method.outputArgs.first().type, method.annotations, 0, "Out");
hs << returnType << " ";
cs << returnType << " ";
}
@ -1004,14 +1066,14 @@ void QDBusXmlToCpp::writeAdaptor(const QString &filename,
if (!method.outputArgs.isEmpty())
cs << ", Q_RETURN_ARG("
<< qtTypeName(method.outputArgs.at(0).name, method.outputArgs.at(0).type, method.annotations,
0, "Out")
<< qtTypeName(method.outputArgs.at(0).location, method.outputArgs.at(0).type,
method.annotations, 0, "Out")
<< ", " << argNames.at(method.inputArgs.size()) << ")";
for (qsizetype i = 0; i < method.inputArgs.size(); ++i)
cs << ", Q_ARG("
<< qtTypeName(method.inputArgs.at(i).name, method.inputArgs.at(i).type, method.annotations,
i, "In")
<< qtTypeName(method.inputArgs.at(i).location, method.inputArgs.at(i).type,
method.annotations, i, "In")
<< ", " << argNames.at(i) << ")";
cs << ");\n";

View File

@ -371,36 +371,43 @@ void tst_qdbusxml2cpp::missingAnnotation_data()
{
QTest::addColumn<QString>("xmlSnippet");
QTest::addColumn<QString>("annotationName");
QTest::addColumn<QString>("location");
QTest::newRow("property")
<< R"(<property type="%1" name="name" access="readwrite"/>)"
<< "org.qtproject.QtDBus.QtTypeName";
<< "org.qtproject.QtDBus.QtTypeName"
<< "7:2";
QTest::newRow("method-in")
<< R"(<method name="Method">
<arg type="%1" name="name" direction="in"/>
</method>)"
<< "org.qtproject.QtDBus.QtTypeName.In0";
<< "org.qtproject.QtDBus.QtTypeName.In0"
<< "8:22";
QTest::newRow("method-out")
<< R"(<method name="Method">
<arg type="%1" name="name" direction="out"/>
</method>)"
<< "org.qtproject.QtDBus.QtTypeName.Out0";
<< "org.qtproject.QtDBus.QtTypeName.Out0"
<< "8:22";
QTest::newRow("signal")
<< R"(<signal name="Signal">
<arg type="%1" name="name"/>
</signal>)"
<< "org.qtproject.QtDBus.QtTypeName.Out0";
<< "org.qtproject.QtDBus.QtTypeName.Out0"
<< "8:22";
QTest::newRow("signal-out")
<< R"(<signal name="Signal">
<arg type="%1" name="name" direction="out"/>
</signal>)"
<< "org.qtproject.QtDBus.QtTypeName.Out0";
<< "org.qtproject.QtDBus.QtTypeName.Out0"
<< "8:22";
}
void tst_qdbusxml2cpp::missingAnnotation()
{
QFETCH(QString, xmlSnippet);
QFETCH(QString, annotationName);
QFETCH(QString, location);
QString type = "(ii)";
QProcess process;
@ -415,9 +422,9 @@ void tst_qdbusxml2cpp::missingAnnotation()
QVERIFY(!errOutput.isEmpty());
// check it did suggest the right annotation
QString expected = R"(qdbusxml2cpp: Got unknown type `%1' processing ''
You should add <annotation name="%2" value="<type>"/> to the XML description for 'name')";
expected = expected.arg(type, annotationName);
QString expected = R"(<standard input>:%3: error: unknown type `%1'
<standard input>:%3: note: you should add <annotation name="%2" value="<type>"/>)";
expected = expected.arg(type, annotationName, location);
QCOMPARE(errOutput, expected);
}