aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/projectexplorer/gccparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/projectexplorer/gccparser.cpp')
-rw-r--r--src/plugins/projectexplorer/gccparser.cpp381
1 files changed, 185 insertions, 196 deletions
diff --git a/src/plugins/projectexplorer/gccparser.cpp b/src/plugins/projectexplorer/gccparser.cpp
index 2fc6c4da44..e4ea4a94cc 100644
--- a/src/plugins/projectexplorer/gccparser.cpp
+++ b/src/plugins/projectexplorer/gccparser.cpp
@@ -30,10 +30,10 @@
#include "projectexplorerconstants.h"
#include "buildmanager.h"
-#include <texteditor/fontsettings.h>
-#include <texteditor/texteditorsettings.h>
#include <utils/qtcassert.h>
+#include <numeric>
+
using namespace ProjectExplorer;
using namespace Utils;
@@ -59,27 +59,96 @@ GccParser::GccParser()
// optional .exe postfix
m_regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN));
QTC_CHECK(m_regExpGccNames.isValid());
+}
+
+Utils::Id GccParser::id()
+{
+ return Utils::Id("ProjectExplorer.OutputParser.Gcc");
+}
+
+QList<OutputLineParser *> GccParser::gccParserSuite()
+{
+ return {new GccParser, new Internal::LldParser, new LdParser};
+}
+
+void GccParser::createOrAmendTask(
+ Task::TaskType type,
+ const QString &description,
+ const QString &originalLine,
+ bool forceAmend,
+ const FilePath &file,
+ int line,
+ const LinkSpecs &linkSpecs
+ )
+{
+ const bool amend = !m_currentTask.isNull() && (forceAmend || isContinuation(originalLine));
+ if (!amend) {
+ flush();
+ m_currentTask = CompileTask(type, description, file, line);
+ m_currentTask.details.append(originalLine);
+ m_linkSpecs = linkSpecs;
+ m_lines = 1;
+ return;
+ }
+
+ LinkSpecs adaptedLinkSpecs = linkSpecs;
+ const int offset = std::accumulate(m_currentTask.details.cbegin(), m_currentTask.details.cend(),
+ 0, [](int total, const QString &line) { return total + line.length() + 1;});
+ for (LinkSpec &ls : adaptedLinkSpecs)
+ ls.startPos += offset;
+ m_linkSpecs << adaptedLinkSpecs;
+ m_currentTask.details.append(originalLine);
+
+ // Check whether the new line is more relevant than the previous ones.
+ if ((m_currentTask.type != Task::Error && type == Task::Error)
+ || (m_currentTask.type == Task::Unknown && type != Task::Unknown)) {
+ m_currentTask.type = type;
+ m_currentTask.summary = description;
+ if (!file.isEmpty()) {
+ m_currentTask.setFile(file);
+ m_currentTask.line = line;
+ }
+ }
+ ++m_lines;
+}
+
+void GccParser::flush()
+{
+ if (m_currentTask.isNull())
+ return;
+
+ // If there is only one line of details, then it is the line that we generated
+ // the summary from. Remove it, because it does not add any information.
+ if (m_currentTask.details.count() == 1)
+ m_currentTask.details.clear();
- appendOutputParser(new Internal::LldParser);
- appendOutputParser(new LdParser);
+ setDetailsFormat(m_currentTask, m_linkSpecs);
+ Task t = m_currentTask;
+ m_currentTask.clear();
+ m_linkSpecs.clear();
+ scheduleTask(t, m_lines, 1);
+ m_lines = 0;
}
-void GccParser::stdError(const QString &line)
+OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat type)
{
- QString lne = rightTrimmed(line);
+ if (type == StdOutFormat) {
+ flush();
+ return Status::NotHandled;
+ }
+
+ const QString lne = rightTrimmed(line);
// Blacklist some lines to not handle them:
if (lne.startsWith(QLatin1String("TeamBuilder ")) ||
lne.startsWith(QLatin1String("distcc["))) {
- IOutputParser::stdError(line);
- return;
+ return Status::NotHandled;
}
// Handle misc issues:
- if (lne.startsWith(QLatin1String("ERROR:")) ||
- lne == QLatin1String("* cpp failed")) {
- newTask(CompileTask(Task::Error, lne /* description */));
- return;
+ if (lne.startsWith(QLatin1String("ERROR:")) || lne == QLatin1String("* cpp failed")) {
+ createOrAmendTask(Task::Error, lne, lne);
+ return Status::InProgress;
}
QRegularExpressionMatch match = m_regExpGccNames.match(lne);
@@ -92,13 +161,22 @@ void GccParser::stdError(const QString &line)
} else if (description.startsWith(QLatin1String("fatal: "))) {
description = description.mid(7);
}
- newTask(CompileTask(type, description));
- return;
+ createOrAmendTask(type, description, lne);
+ return Status::InProgress;
+ }
+
+ match = m_regExpIncluded.match(lne);
+ if (match.hasMatch()) {
+ const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
+ const int lineNo = match.captured(3).toInt();
+ LinkSpecs linkSpecs;
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 1);
+ createOrAmendTask(Task::Unknown, lne.trimmed(), lne, false, filePath, lineNo, linkSpecs);
+ return {Status::InProgress, linkSpecs};
}
match = m_regExp.match(lne);
if (match.hasMatch()) {
- Utils::FilePath filename = Utils::FilePath::fromUserInput(match.captured(1));
int lineno = match.captured(3).toInt();
Task::TaskType type = Task::Unknown;
QString description = match.captured(8);
@@ -113,71 +191,29 @@ void GccParser::stdError(const QString &line)
if (match.captured(5).startsWith(QLatin1Char('#')))
description = match.captured(5) + description;
- newTask(CompileTask(type, description, filename, lineno));
- return;
+ const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
+ LinkSpecs linkSpecs;
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineno, match, 1);
+ createOrAmendTask(type, description, lne, false, filePath, lineno, linkSpecs);
+ return {Status::InProgress, linkSpecs};
}
- match = m_regExpIncluded.match(lne);
- if (match.hasMatch()) {
- newTask(CompileTask(Task::Unknown,
- lne.trimmed() /* description */,
- Utils::FilePath::fromUserInput(match.captured(1)) /* filename */,
- match.captured(3).toInt() /* linenumber */));
- return;
- } else if (lne.startsWith(' ') && !m_currentTask.isNull()) {
- amendDescription(lne, true);
- return;
+ if ((lne.startsWith(' ') && !m_currentTask.isNull()) || isContinuation(lne)) {
+ createOrAmendTask(Task::Unknown, lne, lne, true);
+ return Status::InProgress;
}
- doFlush();
- IOutputParser::stdError(line);
-}
-
-void GccParser::stdOutput(const QString &line)
-{
- doFlush();
- IOutputParser::stdOutput(line);
-}
-
-Core::Id GccParser::id()
-{
- return Core::Id("ProjectExplorer.OutputParser.Gcc");
-}
-
-void GccParser::newTask(const Task &task)
-{
- doFlush();
- m_currentTask = task;
- m_lines = 1;
-}
-
-void GccParser::doFlush()
-{
- if (m_currentTask.isNull())
- return;
- Task t = m_currentTask;
- m_currentTask.clear();
- emit addTask(t, m_lines, 1);
- m_lines = 0;
+ flush();
+ return Status::NotHandled;
}
-void GccParser::amendDescription(const QString &desc, bool monospaced)
+bool GccParser::isContinuation(const QString &newLine) const
{
- if (m_currentTask.isNull())
- return;
- int start = m_currentTask.description.count() + 1;
- m_currentTask.description.append(QLatin1Char('\n'));
- m_currentTask.description.append(desc);
- if (monospaced) {
- QTextLayout::FormatRange fr;
- fr.start = start;
- fr.length = desc.count() + 1;
- fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font());
- fr.format.setFontStyleHint(QFont::Monospace);
- m_currentTask.formats.append(fr);
- }
- ++m_lines;
- return;
+ return !m_currentTask.isNull()
+ && (m_currentTask.details.last().endsWith(':')
+ || m_currentTask.details.last().endsWith(',')
+ || newLine.contains("within this context")
+ || newLine.contains("note:"));
}
// Unit tests:
@@ -598,22 +634,14 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
"C:/Symbian_SDK/epoc32/include/e32cmn.inl:7094: warning: returning reference to temporary")
<< OutputParserTester::STDERR
<< QString() << QString()
- << (Tasks()
- << CompileTask(Task::Unknown,
- "In file included from C:/Symbian_SDK/epoc32/include/e32cmn.h:6792,",
- FilePath::fromUserInput("C:/Symbian_SDK/epoc32/include/e32cmn.h"),
- 6792)
- << CompileTask(Task::Unknown,
- "from C:/Symbian_SDK/epoc32/include/e32std.h:25,",
- FilePath::fromUserInput("C:/Symbian_SDK/epoc32/include/e32std.h"),
- 25)
- << CompileTask(Task::Unknown,
- "In member function 'SSecureId::operator const TSecureId&() const':",
- FilePath::fromUserInput("C:/Symbian_SDK/epoc32/include/e32cmn.inl"))
- << CompileTask(Task::Warning,
- "returning reference to temporary",
- FilePath::fromUserInput("C:/Symbian_SDK/epoc32/include/e32cmn.inl"),
- 7094))
+ << Tasks{CompileTask(Task::Warning,
+ "returning reference to temporary\n"
+ "In file included from C:/Symbian_SDK/epoc32/include/e32cmn.h:6792,\n"
+ " from C:/Symbian_SDK/epoc32/include/e32std.h:25,\n"
+ "C:/Symbian_SDK/epoc32/include/e32cmn.inl: In member function 'SSecureId::operator const TSecureId&() const':\n"
+ "C:/Symbian_SDK/epoc32/include/e32cmn.inl:7094: warning: returning reference to temporary",
+ FilePath::fromUserInput("C:/Symbian_SDK/epoc32/include/e32cmn.inl"),
+ 7094)}
<< QString();
QTest::newRow("QTCREATORBUG-2206")
@@ -632,19 +660,14 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
"/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh:1134:26: warning: no newline at end of file")
<< OutputParserTester::STDERR
<< QString() << QString()
- << (Tasks()
- << CompileTask(Task::Unknown,
- "In file included from /Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h:15,",
- FilePath::fromUserInput("/Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h"),
- 15)
- << CompileTask(Task::Unknown,
- "from <command line>:26:",
- FilePath::fromUserInput("<command line>"),
- 26)
- << CompileTask(Task::Warning,
- "no newline at end of file",
- FilePath::fromUserInput("/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh"),
- 1134))
+ << Tasks{CompileTask(
+ Task::Warning,
+ "no newline at end of file\n"
+ "In file included from /Symbian/SDK/EPOC32/INCLUDE/GCCE/GCCE.h:15,\n"
+ " from <command line>:26:\n"
+ "/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh:1134:26: warning: no newline at end of file",
+ FilePath::fromUserInput("/Symbian/SDK/epoc32/include/variant/Symbian_OS.hrh"),
+ 1134)}
<< QString();
QTest::newRow("Linker fail (release build)")
@@ -677,15 +700,12 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
"./mw.h:4:0: warning: \"STUPID_DEFINE\" redefined")
<< OutputParserTester::STDERR
<< QString() << QString()
- << (Tasks()
- << CompileTask(Task::Unknown,
- "In file included from <command-line>:0:0:",
- FilePath::fromUserInput("<command-line>"),
- 0)
- << CompileTask(Task::Warning,
- "\"STUPID_DEFINE\" redefined",
- FilePath::fromUserInput("./mw.h"),
- 4))
+ << Tasks{CompileTask(
+ Task::Warning,
+ "\"STUPID_DEFINE\" redefined\n"
+ "In file included from <command-line>:0:0:\n"
+ "./mw.h:4:0: warning: \"STUPID_DEFINE\" redefined",
+ FilePath::fromUserInput("./mw.h"), 4)}
<< QString();
QTest::newRow("instanciation with line:column info")
@@ -745,17 +765,14 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
" ^")
<< OutputParserTester::STDERR
<< QString() << QString()
- << (Tasks()
- << CompileTask(Task::Unknown,
- "In file included from /home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp:31:0:",
- FilePath::fromUserInput("/home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp"),
- 31)
- << CompileTask(Task::Error,
- "QtGui/QAction: No such file or directory\n"
- " #include <QtGui/QAction>\n"
- " ^",
- FilePath::fromUserInput(".uic/ui_pluginerrorview.h"),
- 14))
+ << Tasks{CompileTask(
+ Task::Error,
+ "QtGui/QAction: No such file or directory\n"
+ "In file included from /home/code/src/creator/src/libs/extensionsystem/pluginerrorview.cpp:31:0:\n"
+ ".uic/ui_pluginerrorview.h:14:25: fatal error: QtGui/QAction: No such file or directory\n"
+ " #include <QtGui/QAction>\n"
+ " ^",
+ FilePath::fromUserInput(".uic/ui_pluginerrorview.h"), 14)}
<< QString();
QTest::newRow("qtcreatorbug-9195")
@@ -766,26 +783,15 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
"main.cpp:7:22: error: within this context")
<< OutputParserTester::STDERR
<< QString() << QString()
- << (Tasks()
- << CompileTask(Task::Unknown,
- "In file included from /usr/include/qt4/QtCore/QString:1:0,",
- FilePath::fromUserInput("/usr/include/qt4/QtCore/QString"),
- 1)
- << CompileTask(Task::Unknown,
- "from main.cpp:3:",
- FilePath::fromUserInput("main.cpp"),
- 3)
- << CompileTask(Task::Unknown,
- "In function 'void foo()':",
- FilePath::fromUserInput("/usr/include/qt4/QtCore/qstring.h"))
- << CompileTask(Task::Error,
- "'QString::QString(const char*)' is private",
- FilePath::fromUserInput("/usr/include/qt4/QtCore/qstring.h"),
- 597)
- << CompileTask(Task::Error,
- "within this context",
- FilePath::fromUserInput("main.cpp"),
- 7))
+ << Tasks{CompileTask(
+ Task::Error,
+ "'QString::QString(const char*)' is private\n"
+ "In file included from /usr/include/qt4/QtCore/QString:1:0,\n"
+ " from main.cpp:3:\n"
+ "/usr/include/qt4/QtCore/qstring.h: In function 'void foo()':\n"
+ "/usr/include/qt4/QtCore/qstring.h:597:5: error: 'QString::QString(const char*)' is private\n"
+ "main.cpp:7:22: error: within this context",
+ FilePath::fromUserInput("/usr/include/qt4/QtCore/qstring.h"), 597)}
<< QString();
QTest::newRow("ld: Multiple definition error")
@@ -1008,8 +1014,7 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
<< (Tasks()
<< CompileTask(Task::Unknown,
"Note: No relevant classes found. No output generated.",
- FilePath::fromUserInput("/home/qtwebkithelpviewer.h"),
- 0))
+ FilePath::fromUserInput("/home/qtwebkithelpviewer.h")))
<< QString();
QTest::newRow("GCC 9 output")
@@ -1051,78 +1056,62 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
)
<< OutputParserTester::STDERR
<< QString() << QString()
- << Tasks({CompileTask(Task::Unknown,
- "In file included from /usr/include/qt/QtCore/qlocale.h:43,",
- FilePath::fromUserInput("/usr/include/qt/QtCore/qlocale.h"), 43),
- CompileTask(Task::Unknown,
- "from /usr/include/qt/QtCore/qtextstream.h:46,",
- FilePath::fromUserInput("/usr/include/qt/QtCore/qtextstream.h"), 46),
- CompileTask(Task::Unknown,
- "from /qtc/src/shared/proparser/proitems.cpp:31:",
- FilePath::fromUserInput("/qtc/src/shared/proparser/proitems.cpp"), 31),
- CompileTask(Task::Unknown,
- "In constructor ‘QVariant::QVariant(QVariant&&)’:",
- FilePath::fromUserInput("/usr/include/qt/QtCore/qvariant.h"), -1),
- CompileTask(Task::Warning,
- "implicitly-declared ‘constexpr QVariant::Private& QVariant::Private::operator=(const QVariant::Private&)’ is deprecated [-Wdeprecated-copy]\n"
- " 273 | { other.d = Private(); }\n"
- " | ^",
- FilePath::fromUserInput("/usr/include/qt/QtCore/qvariant.h"), 273),
- CompileTask(Task::Unknown,
- "because ‘QVariant::Private’ has user-provided ‘QVariant::Private::Private(const QVariant::Private&)’\n"
- " 399 | inline Private(const Private &other) Q_DECL_NOTHROW\n"
- " | ^~~~~~~)",
- FilePath::fromUserInput("/usr/include/qt/QtCore/qvariant.h"), 399),
- CompileTask(Task::Unknown,
- "In function ‘int test(const shape&, const shape&)’:",
- FilePath::fromUserInput("t.cc"), -1),
- CompileTask(Task::Error,
- "no match for ‘operator+’ (operand types are ‘boxed_value<double>’ and ‘boxed_value<double>’)\n"
- " 14 | return (width(s1) * height(s1)\n"
- " | ~~~~~~~~~~~~~~~~~~~~~~\n"
- " | |\n"
- " | boxed_value<[...]>\n"
- " 15 | + width(s2) * height(s2));\n"
- " | ^ ~~~~~~~~~~~~~~~~~~~~~~\n"
- " | |\n"
- " | boxed_value<[...]>",
- FilePath::fromUserInput("t.cc"),
- 15),
+ << Tasks{CompileTask(Task::Warning,
+ "implicitly-declared ‘constexpr QVariant::Private& QVariant::Private::operator=(const QVariant::Private&)’ is deprecated [-Wdeprecated-copy]\n"
+ "In file included from /usr/include/qt/QtCore/qlocale.h:43,\n"
+ " from /usr/include/qt/QtCore/qtextstream.h:46,\n"
+ " from /qtc/src/shared/proparser/proitems.cpp:31:\n"
+ "/usr/include/qt/QtCore/qvariant.h: In constructor ‘QVariant::QVariant(QVariant&&)’:\n"
+ "/usr/include/qt/QtCore/qvariant.h:273:25: warning: implicitly-declared ‘constexpr QVariant::Private& QVariant::Private::operator=(const QVariant::Private&)’ is deprecated [-Wdeprecated-copy]\n"
+ " 273 | { other.d = Private(); }\n"
+ " | ^\n"
+ "/usr/include/qt/QtCore/qvariant.h:399:16: note: because ‘QVariant::Private’ has user-provided ‘QVariant::Private::Private(const QVariant::Private&)’\n"
+ " 399 | inline Private(const Private &other) Q_DECL_NOTHROW\n"
+ " | ^~~~~~~)",
+ FilePath::fromUserInput("/usr/include/qt/QtCore/qvariant.h"), 273),
+ CompileTask(Task::Unknown, "In function ‘int test(const shape&, const shape&)’:", FilePath::fromUserInput("t.cc")), // TODO: should be matched by GccParser, rather than LdParser
+ CompileTask(Task::Error,
+ "no match for ‘operator+’ (operand types are ‘boxed_value<double>’ and ‘boxed_value<double>’)\n"
+ "t.cc:15:4: error: no match for ‘operator+’ (operand types are ‘boxed_value<double>’ and ‘boxed_value<double>’)\n"
+ " 14 | return (width(s1) * height(s1)\n"
+ " | ~~~~~~~~~~~~~~~~~~~~~~\n"
+ " | |\n"
+ " | boxed_value<[...]>\n"
+ " 15 | + width(s2) * height(s2));\n"
+ " | ^ ~~~~~~~~~~~~~~~~~~~~~~\n"
+ " | |\n"
+ " | boxed_value<[...]>",
+ FilePath::fromUserInput("t.cc"),
+ 15),
CompileTask(Task::Error,
"‘string’ in namespace ‘std’ does not name a type\n"
+ "incomplete.c:1:6: error: ‘string’ in namespace ‘std’ does not name a type\n"
" 1 | std::string test(void)\n"
- " | ^~~~~~",
- FilePath::fromUserInput("incomplete.c"),
- 1),
- CompileTask(Task::Unknown,
- "‘std::string’ is defined in header ‘<string>’; did you forget to ‘#include <string>’?\n"
+ " | ^~~~~~\n"
+ "incomplete.c:1:1: note: ‘std::string’ is defined in header ‘<string>’; did you forget to ‘#include <string>’?\n"
" +++ |+#include <string>\n"
" 1 | std::string test(void)",
FilePath::fromUserInput("incomplete.c"),
1),
- CompileTask(Task::Unknown,
- "In function ‘caller’:",
- FilePath::fromUserInput("param-type-mismatch.c"),
- -1),
+ CompileTask(Task::Unknown, "In function ‘caller’:", FilePath::fromUserInput("param-type-mismatch.c")), // TODO: should be matched by GccParser, rather than LdParser
CompileTask(Task::Warning,
"passing argument 2 of ‘callee’ makes pointer from integer without a cast [-Wint-conversion]\n"
+ "param-type-mismatch.c:5:24: warning: passing argument 2 of ‘callee’ makes pointer from integer without a cast [-Wint-conversion]\n"
" 5 | return callee(first, second, third);\n"
" | ^~~~~~\n"
" | |\n"
- " | int",
- FilePath::fromUserInput("param-type-mismatch.c"), 5),
- CompileTask(Task::Unknown,
- "expected ‘const char *’ but argument is of type ‘int’\n"
+ " | int\n"
+ "param-type-mismatch.c:1:40: note: expected ‘const char *’ but argument is of type ‘int’\n"
" 1 | extern int callee(int one, const char *two, float three);\n"
" | ~~~~~~~~~~~~^~~",
- FilePath::fromUserInput("param-type-mismatch.c"), 1)})
+ FilePath::fromUserInput("param-type-mismatch.c"), 5)}
<< QString();
}
void ProjectExplorerPlugin::testGccOutputParsers()
{
OutputParserTester testbench;
- testbench.appendOutputParser(new GccParser);
+ testbench.setLineParsers(GccParser::gccParserSuite());
QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks);