diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp index 59c29e7fa5f..cf00b5ee3fa 100644 --- a/src/libs/utils/outputformatter.cpp +++ b/src/libs/utils/outputformatter.cpp @@ -342,6 +342,7 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out = d->nextParser->handleLine(text, outputTypeForParser(d->nextParser, format)); switch (res.status) { case OutputLineParser::Status::Done: + d->nextParser->flush(); d->nextParser = nullptr; return res; case OutputLineParser::Status::InProgress: @@ -359,6 +360,7 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out = parser->handleLine(text, outputTypeForParser(parser, format)); switch (res.status) { case OutputLineParser::Status::Done: + parser->flush(); involvedParsers << parser; return res; case OutputLineParser::Status::InProgress: diff --git a/src/plugins/projectexplorer/gccparser.cpp b/src/plugins/projectexplorer/gccparser.cpp index 48c34cd5912..3e61df45dee 100644 --- a/src/plugins/projectexplorer/gccparser.cpp +++ b/src/plugins/projectexplorer/gccparser.cpp @@ -51,8 +51,22 @@ public: if (!match.hasMatch()) return {}; + // Quick plausability test: If there's no slashes or dots, it's probably not a file. + const QString possibleFile = match.captured("file"); + if (!possibleFile.contains('/') && !possibleFile.contains("'\\") + && !possibleFile.contains('.')) { + return {}; + } + + // Defer to the LdParser for some file types + if (possibleFile.endsWith(".o") || possibleFile.endsWith(".a") + || possibleFile.endsWith("dll") || possibleFile.contains(".so") + || possibleFile.endsWith("ld") || possibleFile.endsWith("ranlib")) { + return {}; + } + Data data; - data.rawFilePath = match.captured("file"); + data.rawFilePath = possibleFile; data.fileOffset = match.capturedStart("file"); data.line = match.captured("line").toInt(); data.column = match.captured("column").toInt(); @@ -90,13 +104,13 @@ private: const QString typePrefix = "(?:fatal |#)"; const QString fullTypeString = QString::fromLatin1("(?%1?%2:?\\s)").arg(typePrefix, type); - const QString lineAndOptionalColumn = "(?:(?\\d+)(?::(?\\d+))?)"; + const QString optionalLineAndColumn = "(?:(?:(?\\d+)(?::(?\\d+))?):)?"; const QString binaryLocation = "\\(.*\\)"; // E.g. "(.text+0x40)" const QString fullLocation = QString::fromLatin1("%1(?:%2|%3)") - .arg(filePattern(), lineAndOptionalColumn, binaryLocation); + .arg(filePattern(), optionalLineAndColumn, binaryLocation); const QString description = "(?[^\\s].+)"; - return QString::fromLatin1("^%1:\\s+%2?%3$").arg(fullLocation, fullTypeString, description); + return QString::fromLatin1("^%1\\s+%2?%3$").arg(fullLocation, fullTypeString, description); } }; } // namespace @@ -105,18 +119,10 @@ GccParser::GccParser() { setObjectName(QLatin1String("GCCParser")); - m_regExpScope.setPattern(QLatin1Char('^') + filePattern() - + "(?:(\\d+):)?(\\d+:)?\\s+((?:In .*(?:function|constructor) .*|At global scope|At top level):)$"); - QTC_CHECK(m_regExpScope.isValid()); - m_regExpIncluded.setPattern(QString::fromLatin1("\\bfrom\\s") + filePattern() + QLatin1String("(\\d+)(:\\d+)?[,:]?$")); QTC_CHECK(m_regExpIncluded.isValid()); - m_regExpInlined.setPattern(QString::fromLatin1("\\binlined from\\s.* at ") - + filePattern() + "(\\d+)(:\\d+)?[,:]?$"); - QTC_CHECK(m_regExpInlined.isValid()); - m_regExpCc1plus.setPattern(QLatin1Char('^') + "cc1plus.*(error|warning): ((?:" + filePattern() + " No such file or directory)?.*)"); QTC_CHECK(m_regExpCc1plus.isValid()); @@ -177,8 +183,13 @@ OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat const QString lne = rightTrimmed(line); // Blacklist some lines to not handle them: - if (lne.startsWith(QLatin1String("TeamBuilder ")) || - lne.startsWith(QLatin1String("distcc["))) { + if (lne.startsWith(QLatin1String("TeamBuilder ")) + || lne.startsWith(QLatin1String("distcc[")) + || lne.contains("undefined reference") + || lne.contains("undefined symbol") + || lne.contains("duplicate symbol") + || lne.contains("multiple definition") + || lne.contains("ar: creating")) { return Status::NotHandled; } @@ -205,10 +216,6 @@ OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat qCDebug(gccParserLog) << "checking regex" << m_regExpIncluded.pattern(); match = m_regExpIncluded.match(lne); - if (!match.hasMatch()) { - qCDebug(gccParserLog) << "checking regex" << m_regExpInlined.pattern(); - match = m_regExpInlined.match(lne); - } if (match.hasMatch()) { const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured("file"))); const int lineNo = match.captured(2).toInt(); @@ -229,7 +236,6 @@ OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat if (!filePath.isEmpty()) addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, -1, match, 3); gccCreateOrAmendTask(type, match.captured(2), lne, false, filePath, -1, 0, linkSpecs); - flush(); return {Status::Done, linkSpecs}; } @@ -243,20 +249,6 @@ OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat return {Status::InProgress, linkSpecs}; } - qCDebug(gccParserLog) << "checking regex" << m_regExpScope.pattern(); - match = m_regExpScope.match(lne); - if (match.hasMatch()) { - const int lineno = match.captured(2).toInt(); - const int column = match.captured(3).toInt(); - const QString description = match.captured(4); - const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured("file"))); - LinkSpecs linkSpecs; - addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineno, match, "file"); - gccCreateOrAmendTask( - Task::Unknown, description, lne, false, filePath, lineno, column, linkSpecs); - return {Status::InProgress, linkSpecs}; - } - if ((lne.startsWith(' ') && !currentTask().isNull()) || isContinuation(lne)) { gccCreateOrAmendTask(Task::Unknown, lne, lne, true); return Status::InProgress; @@ -439,8 +431,7 @@ void ProjectExplorerTest::testGccOutputParsers_data() FilePath::fromUserInput("C:\\temp\\test\\untitled8/main.cpp"), 8, 0, formatRanges) - << CompileTask(Task::Error, - "collect2: ld returned 1 exit status")) + << CompileTask(Task::Error, "collect2: ld returned 1 exit status")) << QString(); formatRanges.clear(); @@ -465,8 +456,7 @@ void ProjectExplorerTest::testGccOutputParsers_data() FilePath::fromUserInput("C:\\temp\\test\\untitled8/main.cpp"), -1, 0, formatRanges) - << CompileTask(Task::Error, - "collect2: ld returned 1 exit status")) + << CompileTask(Task::Error, "collect2: ld returned 1 exit status")) << QString(); QTest::newRow("linker: dll format not recognized") @@ -989,7 +979,7 @@ void ProjectExplorerTest::testGccOutputParsers_data() << formatRange(31, 28) << formatRange(59, 23, "olpfile:///home/user/test/foo.cpp::2::-1") << formatRange(82, 34)) - << CompileTask(Task::Unknown, + << CompileTask(Task::Error, "first defined here", FilePath::fromUserInput("/home/user/test/bar.cpp"), 4) @@ -1007,7 +997,7 @@ void ProjectExplorerTest::testGccOutputParsers_data() << CompileTask(Task::Error, "multiple definition of `foo'", FilePath::fromUserInput("foo.o"), -1) - << CompileTask(Task::Unknown, + << CompileTask(Task::Error, "first defined here", FilePath::fromUserInput("bar.o"), -1) << CompileTask(Task::Error, @@ -1465,6 +1455,37 @@ void ProjectExplorerTest::testGccOutputParsers_data() << Tasks{CompileTask(Task::Error, "blubb", "/home/tim/path/to/sources/and/more.h", 15, 22)} << QString(); + + QTest::newRow("no line number") + << QString::fromUtf8("In file included from /data/dev/creator/src/libs/utils/aspects.cpp:12:\n" + "/data/dev/creator/src/libs/utils/layoutbuilder.h: In instantiation of ‘Layouting::BuilderItem::I::I(const Inner&) [with Inner = Utils::BaseAspect; X = Layouting::Row; XInterface = Layouting::LayoutInterface]’:\n" + "/data/dev/creator/src/libs/utils/aspects.cpp:3454:13: required from here\n" + "/data/dev/creator/src/libs/utils/layoutbuilder.h:79:51: error: use of deleted function ‘Utils::BaseAspect::BaseAspect(const Utils::BaseAspect&)’\n" + " 79 | apply = [p](XInterface *x) { doit_nest(x, p); };\n" + " | ~~~~~~~~^~~~~") + << OutputParserTester::STDERR + << QString() << QString() + << (Tasks{compileTask( + Task::Error, + "use of deleted function ‘Utils::BaseAspect::BaseAspect(const Utils::BaseAspect&)’\n" + "In file included from /data/dev/creator/src/libs/utils/aspects.cpp:12:\n" + "/data/dev/creator/src/libs/utils/layoutbuilder.h: In instantiation of ‘Layouting::BuilderItem::I::I(const Inner&) [with Inner = Utils::BaseAspect; X = Layouting::Row; XInterface = Layouting::LayoutInterface]’:\n" + "/data/dev/creator/src/libs/utils/aspects.cpp:3454:13: required from here\n" + "/data/dev/creator/src/libs/utils/layoutbuilder.h:79:51: error: use of deleted function ‘Utils::BaseAspect::BaseAspect(const Utils::BaseAspect&)’\n" + " 79 | apply = [p](XInterface *x) { doit_nest(x, p); };\n" + " | ~~~~~~~~^~~~~", + FilePath::fromUserInput("/data/dev/creator/src/libs/utils/aspects.cpp"), 3454, 13, + QVector{ + formatRange(82, 22), + formatRange(104, 44, "olpfile:///data/dev/creator/src/libs/utils/aspects.cpp::12::-1"), + formatRange(148, 5), + formatRange(153, 48, "olpfile:///data/dev/creator/src/libs/utils/layoutbuilder.h::0::-1"), + formatRange(201, 177), + formatRange(378, 44, "olpfile:///data/dev/creator/src/libs/utils/aspects.cpp::3454::-1"), + formatRange(422, 31), + formatRange(453, 48, "olpfile:///data/dev/creator/src/libs/utils/layoutbuilder.h::79::-1"), + formatRange(501, 228)})}) + << QString(); } void ProjectExplorerTest::testGccOutputParsers() diff --git a/src/plugins/projectexplorer/gccparser.h b/src/plugins/projectexplorer/gccparser.h index 236b4d05707..1c8431c412a 100644 --- a/src/plugins/projectexplorer/gccparser.h +++ b/src/plugins/projectexplorer/gccparser.h @@ -38,9 +38,7 @@ private: bool isContinuation(const QString &newLine) const override; - QRegularExpression m_regExpScope; QRegularExpression m_regExpIncluded; - QRegularExpression m_regExpInlined; QRegularExpression m_regExpGccNames; QRegularExpression m_regExpCc1plus; }; diff --git a/src/plugins/projectexplorer/ldparser.cpp b/src/plugins/projectexplorer/ldparser.cpp index 67784e2a8da..ea7521bd0e2 100644 --- a/src/plugins/projectexplorer/ldparser.cpp +++ b/src/plugins/projectexplorer/ldparser.cpp @@ -39,46 +39,49 @@ Utils::OutputLineParser::Result LdParser::handleLine(const QString &line, Utils: return Status::NotHandled; QString lne = rightTrimmed(line); - if (!lne.isEmpty() && !lne.at(0).isSpace() && !currentTask().isNull()) { - flush(); - return Status::NotHandled; - } - if (lne.startsWith(QLatin1String("TeamBuilder ")) || lne.startsWith(QLatin1String("distcc[")) || lne.contains(QLatin1String("ar: creating "))) { return Status::NotHandled; } + const auto getStatus = [&lne] { return lne.endsWith(':') ? Status::InProgress : Status::Done; }; + // ld on macOS - if (lne.startsWith("Undefined symbols for architecture") && lne.endsWith(":")) { + if (lne.startsWith("Undefined symbols for architecture") && getStatus() == Status::InProgress) { createOrAmendTask(Task::Error, lne, line); return Status::InProgress; } - if (!currentTask().isNull() && lne.startsWith(" ")) { + + if (!currentTask().isNull() && isContinuation(line)) { static const QRegularExpression locRegExp(" (?\\S+) in (?\\S+)"); const QRegularExpressionMatch match = locRegExp.match(lne); LinkSpecs linkSpecs; Utils::FilePath filePath; + bool handle = false; if (match.hasMatch()) { + handle = true; filePath = absoluteFilePath(Utils::FilePath::fromString(match.captured("file"))); addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, 0, match, "file"); currentTask().setFile(filePath); + } else { + handle = !lne.isEmpty() && lne.at(0).isSpace(); + } + if (handle) { + createOrAmendTask(Task::Unknown, {}, line, true, filePath); + return {Status::InProgress, linkSpecs}; } - createOrAmendTask(Task::Unknown, {}, line, true, filePath); - return {Status::InProgress, linkSpecs}; } if (lne.startsWith("collect2:") || lne.startsWith("collect2.exe:")) { - scheduleTask(CompileTask(Task::Error, lne /* description */), 1); - return Status::Done; + createOrAmendTask(Task::Error, lne, line); + return getStatus(); } QRegularExpressionMatch match = m_ranlib.match(lne); if (match.hasMatch()) { - QString description = match.captured(2); - scheduleTask(CompileTask(Task::Warning, description), 1); - return Status::Done; + createOrAmendTask(Task::Warning, match.captured(2), line); + return getStatus(); } match = m_regExpGccNames.match(lne); @@ -91,8 +94,8 @@ Utils::OutputLineParser::Result LdParser::handleLine(const QString &line, Utils: } else if (description.startsWith(QLatin1String("fatal: "))) { description = description.mid(7); } - scheduleTask(CompileTask(type, description), 1); - return Status::Done; + createOrAmendTask(type, description, line); + return getStatus(); } match = m_regExpLinker.match(lne); @@ -101,41 +104,47 @@ Utils::OutputLineParser::Result LdParser::handleLine(const QString &line, Utils: int lineno = match.captured(7).toInt(&ok); if (!ok) lineno = -1; - Utils::FilePath filename + Utils::FilePath filePath = absoluteFilePath(Utils::FilePath::fromUserInput(match.captured(1))); int capIndex = 1; const QString sourceFileName = match.captured(4); if (!sourceFileName.isEmpty() && !sourceFileName.startsWith(QLatin1String("(.text")) && !sourceFileName.startsWith(QLatin1String("(.data"))) { - filename = absoluteFilePath(Utils::FilePath::fromUserInput(sourceFileName)); + filePath = absoluteFilePath(Utils::FilePath::fromUserInput(sourceFileName)); capIndex = 4; } QString description = match.captured(8).trimmed(); - Task::TaskType type = Task::Error; - if (description.startsWith(QLatin1String("first defined here")) || - description.startsWith(QLatin1String("note:"), Qt::CaseInsensitive)) { - type = Task::Unknown; - } else if (description.startsWith(QLatin1String("warning: "), Qt::CaseInsensitive)) { - type = Task::Warning; - description = description.mid(9); - } static const QStringList keywords{ "File format not recognized", "undefined reference", + "multiple definition", "first defined here", "feupdateenv is not implemented and will always fail", // yes, this is quite special ... }; const auto descriptionContainsKeyword = [&description](const QString &keyword) { return description.contains(keyword); }; - if (Utils::anyOf(keywords, descriptionContainsKeyword)) { + Task::TaskType type = Task::Unknown; + const bool hasKeyword = Utils::anyOf(keywords, descriptionContainsKeyword); + if (description.startsWith(QLatin1String("warning: "), Qt::CaseInsensitive)) { + type = Task::Warning; + description = description.mid(9); + } else if (hasKeyword) { + type = Task::Error; + } + if (hasKeyword || filePath.fileName().endsWith(".o")) { LinkSpecs linkSpecs; - addLinkSpecForAbsoluteFilePath(linkSpecs, filename, lineno, match, capIndex); - scheduleTask(CompileTask(type, description, filename, lineno), 1); - return {Status::Done, linkSpecs}; + addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineno, match, capIndex); + createOrAmendTask(type, description, line, false, filePath, lineno, 0, linkSpecs); + return {getStatus(), linkSpecs}; } } return Status::NotHandled; } + +bool LdParser::isContinuation(const QString &line) const +{ + return currentTask().details.last().endsWith(':') || (!line.isEmpty() && line.at(0).isSpace()); +} diff --git a/src/plugins/projectexplorer/ldparser.h b/src/plugins/projectexplorer/ldparser.h index ce2114b5f07..d8c23747898 100644 --- a/src/plugins/projectexplorer/ldparser.h +++ b/src/plugins/projectexplorer/ldparser.h @@ -17,6 +17,7 @@ public: LdParser(); private: Result handleLine(const QString &line, Utils::OutputFormat type) override; + bool isContinuation(const QString &line) const override; QRegularExpression m_ranlib; QRegularExpression m_regExpLinker;