From f9058a75347bf699c452b1f6cdf637d48429b7a2 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 5 Nov 2020 18:23:32 +0100 Subject: [PATCH] moc: output errors and warnings in a way that matches compilers gcc, clang, and MSVC all use lowercase "warning:", "error:" and "note:". Follow that standard. Also, include a column number; just print 1, as the Symbol doesn't give us a column number, and searching backwards for a newline seems overkill. This fixes IDE integrations that parse compiler output using regular expressions. The test checks for moc output, but most tests were so far only running on Linux systems. Expand this to Unix for most tests, which then includes macOS. Change-Id: I0a6151cc0dc50e52ca72ff8048a45213aebdb3a8 Reviewed-by: Lars Knoll --- src/tools/moc/parser.cpp | 20 +++---- tests/auto/tools/moc/tst_moc.cpp | 100 +++++++++++++++---------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/tools/moc/parser.cpp b/src/tools/moc/parser.cpp index 068f75d4bd..4b67e3a99a 100644 --- a/src/tools/moc/parser.cpp +++ b/src/tools/moc/parser.cpp @@ -40,9 +40,9 @@ Symbol::LexemStore Symbol::lexemStore; static const char *error_msg = nullptr; #ifdef Q_CC_MSVC -#define ErrorFormatString "%s(%d): " +#define ErrorFormatString "%s(%d:%d): " #else -#define ErrorFormatString "%s:%d: " +#define ErrorFormatString "%s:%d:%d: " #endif void Parser::error(int rollback) { @@ -51,24 +51,24 @@ void Parser::error(int rollback) { } void Parser::error(const char *msg) { if (msg || error_msg) - fprintf(stderr, ErrorFormatString "Error: %s\n", - currentFilenames.top().constData(), symbol().lineNum, msg?msg:error_msg); + fprintf(stderr, ErrorFormatString "error: %s\n", + currentFilenames.top().constData(), symbol().lineNum, 1, msg?msg:error_msg); else - fprintf(stderr, ErrorFormatString "Parse error at \"%s\"\n", - currentFilenames.top().constData(), symbol().lineNum, symbol().lexem().data()); + fprintf(stderr, ErrorFormatString "error: Parse error at \"%s\"\n", + currentFilenames.top().constData(), symbol().lineNum, 1, symbol().lexem().data()); exit(EXIT_FAILURE); } void Parser::warning(const char *msg) { if (displayWarnings && msg) - fprintf(stderr, ErrorFormatString "Warning: %s\n", - currentFilenames.top().constData(), qMax(0, index > 0 ? symbol().lineNum : 0), msg); + fprintf(stderr, ErrorFormatString "warning: %s\n", + currentFilenames.top().constData(), qMax(0, index > 0 ? symbol().lineNum : 0), 1, msg); } void Parser::note(const char *msg) { if (displayNotes && msg) - fprintf(stderr, ErrorFormatString "Note: %s\n", - currentFilenames.top().constData(), qMax(0, index > 0 ? symbol().lineNum : 0), msg); + fprintf(stderr, ErrorFormatString "note: %s\n", + currentFilenames.top().constData(), qMax(0, index > 0 ? symbol().lineNum : 0), 1, msg); } QT_END_NAMESPACE diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index d6eb76feb7..1f74c6cccd 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -847,7 +847,7 @@ void tst_Moc::warnOnExtraSignalSlotQualifiaction() #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; const QString header = m_sourceDirectory + QStringLiteral("/extraqualification.h"); proc.start(m_moc, QStringList(header)); @@ -857,10 +857,10 @@ void tst_Moc::warnOnExtraSignalSlotQualifiaction() QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + - QString(":43: Warning: Function declaration Test::badFunctionDeclaration contains extra qualification. Ignoring as signal or slot.\n") + - header + QString(":46: Warning: parsemaybe: Function declaration Test::anotherOne contains extra qualification. Ignoring as signal or slot.\n")); + QString(":43:1: warning: Function declaration Test::badFunctionDeclaration contains extra qualification. Ignoring as signal or slot.\n") + + header + QString(":46:1: warning: parsemaybe: Function declaration Test::anotherOne contains extra qualification. Ignoring as signal or slot.\n")); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } @@ -1142,7 +1142,7 @@ void tst_Moc::warnOnMultipleInheritance() QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + - QString(":43: Warning: Class Bar inherits from two QObject subclasses QWindow and Foo. This is not supported!\n")); + QString(":43:1: warning: Class Bar inherits from two QObject subclasses QWindow and Foo. This is not supported!\n")); #else QSKIP("Only tested on linux/gcc"); #endif @@ -1206,7 +1206,7 @@ void tst_Moc::forgottenQInterface() QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + - QString(":45: Warning: Class Test implements the interface MyInterface but does not list it in Q_INTERFACES. qobject_cast to MyInterface will not work!\n")); + QString(":45:1: warning: Class Test implements the interface MyInterface but does not list it in Q_INTERFACES. qobject_cast to MyInterface will not work!\n")); #else QSKIP("Only tested on linux/gcc"); #endif @@ -1320,7 +1320,7 @@ void tst_Moc::templateGtGt() #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; proc.start(m_moc, QStringList(m_sourceDirectory + QStringLiteral("/template-gtgt.h"))); QVERIFY(proc.waitForFinished()); @@ -1330,13 +1330,13 @@ void tst_Moc::templateGtGt() QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QVERIFY(mocWarning.isEmpty()); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::defineMacroViaCmdline() { -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; @@ -1350,13 +1350,13 @@ void tst_Moc::defineMacroViaCmdline() QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::defineMacroViaForcedInclude() { -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; @@ -1370,13 +1370,13 @@ void tst_Moc::defineMacroViaForcedInclude() QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::defineMacroViaForcedIncludeRelative() { -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; @@ -1390,14 +1390,14 @@ void tst_Moc::defineMacroViaForcedIncludeRelative() QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::environmentIncludePaths_data() { -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QTest::addColumn("cmdline"); QTest::addColumn("varname"); @@ -1411,7 +1411,7 @@ void tst_Moc::environmentIncludePaths_data() void tst_Moc::environmentIncludePaths() { -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QFETCH(QString, cmdline); QFETCH(QString, varname); @@ -1436,7 +1436,7 @@ void tst_Moc::environmentIncludePaths() QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } @@ -1600,7 +1600,7 @@ void tst_Moc::warnOnPropertyWithoutREAD() #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; const QString header = m_sourceDirectory + QStringLiteral("/warn-on-property-without-read.h"); proc.start(m_moc, QStringList(header)); @@ -1610,9 +1610,9 @@ void tst_Moc::warnOnPropertyWithoutREAD() QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + - QString(":36: Warning: Property declaration foo has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.\n")); + QString(":36:1: warning: Property declaration foo has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.\n")); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } @@ -1711,7 +1711,7 @@ void tst_Moc::warnOnVirtualSignal() #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; const QString header = m_sourceDirectory + QStringLiteral("/pure-virtual-signals.h"); proc.start(m_moc, QStringList(header)); @@ -1720,10 +1720,10 @@ void tst_Moc::warnOnVirtualSignal() QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); - QCOMPARE(mocWarning, header + QString(":38: Warning: Signals cannot be declared virtual\n") + - header + QString(":40: Warning: Signals cannot be declared virtual\n")); + QCOMPARE(mocWarning, header + QString(":38:1: warning: Signals cannot be declared virtual\n") + + header + QString(":40:1: warning: Signals cannot be declared virtual\n")); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } @@ -2060,7 +2060,7 @@ void tst_Moc::warnings_data() << QStringList() << 0 << QString() - << QString("standard input:0: Note: No relevant classes found. No output generated."); + << QString("standard input:0:1: note: No relevant classes found. No output generated."); // passing "-nn" should suppress "no relevant classes" note QTest::newRow("-nn") @@ -2084,7 +2084,7 @@ void tst_Moc::warnings_data() << QStringList() << 0 << QString("IGNORE_ALL_STDOUT") - << QString("standard input:1: Warning: Property declaration x has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid."); + << QString("standard input:1:1: warning: Property declaration x has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid."); // This should output a warning QTest::newRow("Duplicate property warning") @@ -2092,7 +2092,7 @@ void tst_Moc::warnings_data() << QStringList() << 0 << QString("IGNORE_ALL_STDOUT") - << QString("standard input:1: Warning: The property 'x' is defined multiple times in class X."); + << QString("standard input:1:1: warning: The property 'x' is defined multiple times in class X."); // Passing "-nn" should NOT suppress the warning QTest::newRow("Invalid property warning with -nn") @@ -2100,7 +2100,7 @@ void tst_Moc::warnings_data() << (QStringList() << "-nn") << 0 << QString("IGNORE_ALL_STDOUT") - << QString("standard input:1: Warning: Property declaration x has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid."); + << QString("standard input:1:1: warning: Property declaration x has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid."); // Passing "-nw" should suppress the warning QTest::newRow("Invalid property warning with -nw") @@ -2116,7 +2116,7 @@ void tst_Moc::warnings_data() << QStringList() << 1 << QString() - << QString("standard input:1: Error: Class contains Q_OBJECT macro but does not inherit from QObject"); + << QString("standard input:1:1: error: Class contains Q_OBJECT macro but does not inherit from QObject"); // "-nn" should not suppress the error QTest::newRow("Does not inherit QObject with -nn") @@ -2124,7 +2124,7 @@ void tst_Moc::warnings_data() << (QStringList() << "-nn") << 1 << QString() - << QString("standard input:1: Error: Class contains Q_OBJECT macro but does not inherit from QObject"); + << QString("standard input:1:1: error: Class contains Q_OBJECT macro but does not inherit from QObject"); // "-nw" should not suppress the error QTest::newRow("Does not inherit QObject with -nw") @@ -2132,7 +2132,7 @@ void tst_Moc::warnings_data() << (QStringList() << "-nw") << 1 << QString() - << QString("standard input:1: Error: Class contains Q_OBJECT macro but does not inherit from QObject"); + << QString("standard input:1:1: error: Class contains Q_OBJECT macro but does not inherit from QObject"); QTest::newRow("Warning on invalid macro") << QByteArray("#define Foo(a, b)\n class X : public QObject { Q_OBJECT }; \n Foo(a) \n Foo(a,b,c) \n") @@ -2146,63 +2146,63 @@ void tst_Moc::warnings_data() << QStringList() << 1 << QString() - << QString("standard input:5: Error: Class declaration lacks Q_OBJECT macro."); + << QString("standard input:5:1: error: Class declaration lacks Q_OBJECT macro."); QTest::newRow("Namespace declaration lacks Q_NAMESPACE macro.") << QByteArray("namespace X {\nQ_CLASSINFO(\"key\",\"value\")\nenum class MyEnum {Key1 = 1}\nQ_ENUMS(MyEnum)\n}\n") << QStringList() << 1 << QString() - << QString("standard input:1: Error: Namespace declaration lacks Q_NAMESPACE macro."); + << QString("standard input:1:1: error: Namespace declaration lacks Q_NAMESPACE macro."); QTest::newRow("Wrong Q_ENUM context.") << QByteArray("namespace X {\nQ_NAMESPACE\n\nenum class MyEnum {Key1 = 1}\nQ_ENUM(MyEnum)\n}\n") << QStringList() << 1 << QString() - << QString("standard input:5: Error: Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead"); + << QString("standard input:5:1: error: Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead"); QTest::newRow("Wrong Q_FLAG context.") << QByteArray("namespace X {\nQ_NAMESPACE\n\nenum class MyEnum {Key1 = 1}\nQ_FLAG(MyEnum)\n}\n") << QStringList() << 1 << QString() - << QString("standard input:5: Error: Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead"); + << QString("standard input:5:1: error: Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead"); QTest::newRow("Wrong Q_ENUM_NS context.") << QByteArray("class X {\nQ_GADGET\n\nenum class MyEnum {Key1 = 1}\nQ_ENUM_NS(MyEnum)\n};\n") << QStringList() << 1 << QString() - << QString("standard input:5: Error: Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead"); + << QString("standard input:5:1: error: Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead"); QTest::newRow("Wrong Q_FLAG_NS context.") << QByteArray("class X {\nQ_GADGET\n\nenum class MyEnum {Key1 = 1}\nQ_FLAG_NS(MyEnum)\n};\n") << QStringList() << 1 << QString() - << QString("standard input:5: Error: Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead"); + << QString("standard input:5:1: error: Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead"); QTest::newRow("Invalid macro definition") << QByteArray("#define Foo(a, b, c) a b c #a #b #c a##b##c #d\n Foo(45, 42, 39);") << QStringList() << 1 << QString("IGNORE_ALL_STDOUT") - << QString(":2: Error: '#' is not followed by a macro parameter"); + << QString(":2:1: error: '#' is not followed by a macro parameter"); QTest::newRow("QTBUG-46210: crash on invalid macro invocation") << QByteArray("#define Foo(a, b, c) a b c #a #b #c a##b##c\n Foo(45);") << QStringList() << 1 << QString("IGNORE_ALL_STDOUT") - << QString(":2: Error: Macro invoked with too few parameters for a use of '#'"); + << QString(":2:1: error: Macro invoked with too few parameters for a use of '#'"); QTest::newRow("QTBUG-54609: crash on invalid input") << QByteArray::fromBase64("EAkJCQkJbGFzcyBjbGFzcyBiYWkcV2kgTUEKcGYjZGVmaW5lIE1BKFEs/4D/FoQ=") << QStringList() << 1 << QString("IGNORE_ALL_STDOUT") - << QString(":-1: Error: Unexpected character in macro argument list."); + << QString(":-1:1: error: Unexpected character in macro argument list."); QTest::newRow("Missing header warning") << QByteArray("class X : public QObject { Q_OBJECT };") @@ -2216,22 +2216,22 @@ void tst_Moc::warnings_data() << QStringList() << 0 << QString() - << QString("standard input:1: Note: No relevant classes found. No output generated."); + << QString("standard input:1:1: note: No relevant classes found. No output generated."); QTest::newRow("Q_PLUGIN_METADATA: invalid file") << QByteArray("class X { \n Q_PLUGIN_METADATA(FILE \"does.not.exists\") \n };") << QStringList() << 1 << QString() - << QString("standard input:2: Error: Plugin Metadata file \"does.not.exists\" does not exist. Declaration will be ignored"); + << QString("standard input:2:1: error: Plugin Metadata file \"does.not.exists\" does not exist. Declaration will be ignored"); -#ifdef Q_OS_LINUX // Limit to Linux because the error message is platform-dependent +#ifdef Q_OS_UNIX // Limit to Unix because the error message is platform-dependent QTest::newRow("Q_PLUGIN_METADATA: unreadable file") << QByteArray("class X { \n Q_PLUGIN_METADATA(FILE \".\") \n };") << QStringList() << 1 << QString() - << QString("standard input:2: Error: Plugin Metadata file \".\" could not be opened: file to open is a directory"); + << QString("standard input:2:1: error: Plugin Metadata file \".\" could not be opened: file to open is a directory"); #endif } @@ -2247,9 +2247,9 @@ void tst_Moc::warnings() QFETCH(QString, expectedStdErr); #ifdef Q_CC_MSVC - // for some reasons, moc compiled with MSVC uses a different output format - QRegularExpression lineNumberRe(":(-?\\d+):", QRegularExpression::InvertedGreedinessOption); - expectedStdErr.replace(lineNumberRe, "(\\1):"); + // moc compiled with MSVC uses a different output format to match MSVC compiler style + QRegularExpression lineNumberRe(":(-?\\d+):(\\d+)", QRegularExpression::InvertedGreedinessOption); + expectedStdErr.replace(lineNumberRe, "(\\1:\\2)"); #endif #if QT_CONFIG(process) @@ -3519,7 +3519,7 @@ void tst_Moc::preprocessorOnly() #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; proc.start(m_moc, QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/pp-dollar-signs.h")); QVERIFY(proc.waitForFinished()); @@ -3530,7 +3530,7 @@ void tst_Moc::preprocessorOnly() QVERIFY(mocOut.contains("$$ = parser->createFoo()")); #else - QSKIP("Only tested on linux/gcc"); + QSKIP("Only tested on unix/gcc"); #endif } @@ -3540,7 +3540,7 @@ void tst_Moc::unterminatedFunctionMacro() #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif -#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) +#if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; proc.start(m_moc, QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/unterminated-function-macro.h")); QVERIFY(proc.waitForFinished());